dsh-code-server-app 0.2.11 → 0.2.12

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/README.en.md CHANGED
@@ -182,7 +182,7 @@ state preserved (no full reload). Full evidence and probe scripts: `docs/analysi
182
182
 
183
183
  ## code-server workspace and process lifecycle
184
184
 
185
- - code-server's workspace **follows the active DSH session/workspace**: switching sessions/workspaces while the IDE is open restarts code-server to the new directory
185
+ - code-server's workspace **follows the active DSH session/workspace**: switching sessions/workspaces while the IDE is open moves code-server to the new directory
186
186
  (resolution order: current session cwd → session's workspace.path → recentWorkspace.path → first workspace.path);
187
187
  the opened directory is shown inside code-server (`?folder=<cwd>`, the page reloads when following a switch);
188
188
  implementation note: the iframe `src` must carry `?folder=<cwd>` — code-server's front-end remembers the "last workspace" and restores it by itself;
@@ -190,6 +190,19 @@ state preserved (no full reload). Full evidence and probe scripts: `docs/analysi
190
190
  **Windows path format (verified)**: the `folder` parameter must start with `/` and use forward slashes only, e.g. `/C:/Users/User/Desktop/biss`;
191
191
  a bare Windows path (`C:\...`) is parsed as a URI scheme and the drive letter is stripped (page shows `\Users\User\...` with an empty file tree),
192
192
  while `file:///C:/...` reports "Workspace does not exist".
193
+ - **The switch is lightweight (since 0.2.12)**: a running instance is **not restarted** when the workspace changes — the host
194
+ only updates `state.cwd` and the workbench re-navigates with the new `?folder=` (the workspace directory was always the
195
+ client URL's business; the process cwd only affects the server's own relative-path resolution at spawn time). The switch is
196
+ much faster and no longer throws away the extension host, background tasks or server-side state.
197
+ - In `status`, `cwd` is the current workbench directory; `launchCwd` is the directory the **process was started with**
198
+ (diagnostics only; it does not change on a switch).
199
+ - The trade-off, stated plainly: background processes/terminals started by the IDE in the **old** directory are no longer
200
+ killed automatically (a full restart used to take them with it) — clean them up yourself if needed. That is the same coin
201
+ as "nothing is lost".
202
+ - The trigger does not depend on the tab being visible: **the tab body is not unmounted while the sidebar is collapsed**,
203
+ so it also follows in the background (behaviour deliberately kept in 0.2.12).
204
+ - Regression: `scripts/test-workspace-switch.mjs` (5 assertions: adopting an instance, cwd change keeps pid/status, the
205
+ process stays alive, same-directory idempotence, no-cwd does not switch; it fails again if the old behaviour returns).
193
206
  - Process lifecycle is managed by the host plugin: startup writes `$DSH_HOME/code-server/pid.json`, stop kills the tree (`taskkill /T` or process-group SIGKILL),
194
207
  crash/exit updates status live; after a DSH host restart the plugin **adopts** a still-running instance (verifies pid + `/healthz`), without duplicate start or killing unrelated processes;
195
208
  - `node_modules` and the pack-time artifact `vendor/` are git-ignored; after cloning, follow
@@ -447,7 +460,7 @@ Host/Origin fence and browser auth); in the desktop profile `apps/desktop-host`
447
460
  | Method | Path | Description |
448
461
  |---|---|---|
449
462
  | GET | `/api/code-server/status` | `{ ok, running, status, host, port, pid, cwd, url, version, error, logTail, adopted }` (also `env` environment check and the `setup` compatibility field) |
450
- | POST | `/api/code-server/start` | body `{ cwd? }` (omit cwd to keep the current workspace); idempotent |
463
+ | POST | `/api/code-server/start` | body `{ cwd? }` (omit cwd to keep the current workspace); idempotent; changing cwd while running only **switches the directory, without restarting the process** (0.2.12) |
451
464
  | POST | `/api/code-server/stop` | Stop and recycle the process tree |
452
465
  | POST | `/api/code-server/setup` | **Compatibility no-op**: since 0.1.36 dependencies are installed by the package manager, so this only re-runs the env self-check and returns |
453
466
  | POST | `/api/code-server/open-file` | body `{ file }` — writes the signal consumed by the built-in `dshcs-open-file` extension to open the file in code-server |
@@ -533,8 +546,7 @@ What remains on the plugin side:
533
546
  works). Use `serve: loopback` when you need it.
534
547
  - **`serve: dsh` shares DSH's origin**, so the iframe is not sandboxed there (same-origin plus `allow-same-origin` is
535
548
  escapable by the frame itself); in `loopback` mode the iframe is cross-origin and `sandbox` stays as real protection.
536
- - **Single instance across sessions**: one shared IDE per host; switching cwd requires a restart (the sidebar tab
537
- handles it and hints).
549
+ - **Single instance across sessions**: one shared IDE per host; switching cwd only re-navigates the workbench (since 0.2.12 no process restart, so the old directory's background terminals are not collected).
538
550
  - **Older DSH versions are unsupported (since 0.2.3)**: on a DSH without `sidebarRightTabs` / `sidebarRight` the plugin
539
551
  offers nothing but an upgrade notice on the settings page; older-DSH users should stay on `0.2.2`
540
552
  (`dsh plugin --profile web add dsh-code-server-app@0.2.2`).
package/README.md CHANGED
@@ -182,7 +182,7 @@ DSH 用**资源地址**命名文件,`openFile` 只负责把地址交给右侧栏
182
182
 
183
183
  ## code-server 服务目录与进程生命周期
184
184
 
185
- - code-server 服务目录**跟随活动工作区/会话**:打开期间切换 DSH 会话/工作区,code-server 自动重启到新目录
185
+ - code-server 服务目录**跟随活动工作区/会话**:打开期间切换 DSH 会话/工作区,code-server 自动切到新目录
186
186
  (解析优先级:当前会话 cwd → 会话所属 workspace.path → recentWorkspace.path → 首个 workspace.path);
187
187
  打开目录显示在 code-server 页面内(`?folder=<cwd>`,跟随切换时页面自动重新加载);
188
188
  实现要点:iframe src 必须带 `?folder=<cwd>`——code-server 前端会记住“最近工作区”并自行恢复,
@@ -190,6 +190,15 @@ DSH 用**资源地址**命名文件,`openFile` 只负责把地址交给右侧栏
190
190
  **Windows 路径格式(实测)**:folder 参数必须以 `/` 开头且全部正斜杠,形如 `/C:/Users/User/Desktop/biss`;
191
191
  裸 Windows 路径(`C:\...`)会被前端当 URI scheme 而剥掉盘符(页面显示 `\Users\User\...` 且文件树为空),
192
192
  `file:///C:/...` 形式则报 “Workspace does not exist”。
193
+ - **切换是"轻量"的(0.2.12 起)**:运行中切工作区**不重启 IDE 进程**,host 只把 `state.cwd` 改成新目录,
194
+ 由 workbench 拿新的 `?folder=` 重新导航(工作区目录本来就由客户端 URL 决定,进程 cwd 只影响它自己
195
+ spawn 时的相对路径解析)。因此切换**不再丢**扩展宿主/后台任务/服务端状态,也快得多。
196
+ - `status` 里 `cwd` = 当前 workbench 目录;`launchCwd` = **进程启动时**的目录(诊断用,不随切换变化)。
197
+ - 代价(要说清楚):旧目录里**由 IDE 拉起的后台进程/终端不再被自动杀掉**(以前靠"整进程重启"顺带收走)——
198
+ 需要时手动收;这也是"不丢状态"的同一枚硬币。
199
+ - 触发时机与本标签是否可见无关:**侧栏收起时标签 body 并不卸载**,所以后台也会跟随(0.2.12 明确保留此行为)。
200
+ - 回归:`scripts/test-workspace-switch.mjs`(5 项:接管实例、切目录不改 pid/不换状态、进程存活、
201
+ 同目录幂等、无 cwd 不切换;改回旧行为必挂)。
193
202
  - process 生命周期由 host 插件管理:启动写 `$DSH_HOME/code-server/pid.json`,停止树级终止(taskkill /T 或进程组 SIGKILL),
194
203
  崩溃/退出实时更新状态;DSH host 重启后自动 adopt 仍在运行的实例(校验 pid + /healthz),不重复启动、不误杀别的进程;
195
204
  - `node_modules`、`vendor/` 与 `repack/` 已被 `.gitignore` 排除,推送/克隆仓库后按下方
@@ -449,8 +458,8 @@ desktop profile 由 `apps/desktop-host` 把 `/api/*` 交给同一个 `createShar
449
458
 
450
459
  | 方法 | 路径 | 说明 |
451
460
  |---|---|---|
452
- | GET | `/api/code-server/status` | `{ ok, running, status, host, port, pid, cwd, url, version, error, logTail, adopted }`(另含 `env` 环境检测与 `setup` 兼容字段) |
453
- | POST | `/api/code-server/start` | body `{ cwd? }`(省略 cwd 不切换工作目录);幂等 |
461
+ | GET | `/api/code-server/status` | `{ ok, running, status, host, port, pid, cwd, launchCwd, url, version, error, logTail, adopted }`(另含 `env` 环境检测与 `setup` 兼容字段;`cwd` = 当前 workbench 目录,`launchCwd` = 进程启动目录) |
462
+ | POST | `/api/code-server/start` | body `{ cwd? }`(省略 cwd 不切换工作目录);幂等;运行中切 cwd = **只换目录不重启进程**(0.2.12) |
454
463
  | POST | `/api/code-server/stop` | 停止并回收进程树 |
455
464
  | POST | `/api/code-server/setup` | **兼容空操作**:0.1.36 起依赖由包管理器安装,调用只重新自检 `env` 并返回 |
456
465
  | POST | `/api/code-server/open-file` | body `{ file }` — 写信号文件,由内置扩展 `dshcs-open-file` 在 code-server 中打开 |
@@ -514,7 +523,7 @@ desktop profile 由 `apps/desktop-host` 把 `/api/*` 交给同一个 `createShar
514
523
  → 该模式下 Ports 面板的 **WebSocket** 转发不可用(HTTP 转发正常);需要时用 `serve: loopback`。
515
524
  - **`serve: dsh` 的 iframe 与 DSH 同源** → 该模式不挂 `sandbox`(同源 + `allow-same-origin` 可被 frame 自行摘除);
516
525
  `loopback` 模式跨源,`sandbox` 作为真防护保留。
517
- - **跨会话单实例**:host 级共享一份 IDE;切换 cwd 需重启实例(右侧栏标签自动处理并提示)。
526
+ - **跨会话单实例**:host 级共享一份 IDE;切换 cwd 只换 workbench 目录(0.2.12 起不重启进程,旧目录的后台终端不会被收走)。
518
527
  - **旧版 DSH 不受支持(0.2.3 起)**:没有 `sidebarRightTabs`/`sidebarRight` 的 DSH 上,除设置页一条升级提示外无任何入口;
519
528
  旧版用户请留在 `0.2.2`(`dsh plugin --profile web add dsh-code-server-app@0.2.2`)。
520
529
  - **侧栏标签切换**(0.2.2 起不再重载):DSH 右侧栏只渲染当前激活标签的 body,React 卸载会移走 iframe;
package/lib/index.js CHANGED
@@ -449,6 +449,8 @@ export async function apply(ctx, config) {
449
449
  pid: null,
450
450
  port: cfg.port,
451
451
  cwd: null,
452
+ /** 进程**启动时**的工作目录(诊断用):运行时切工作区只改 cwd,进程 cwd 保持这里不变。 */
453
+ launchCwd: null,
452
454
  version: null,
453
455
  error: null,
454
456
  logTail: '',
@@ -507,6 +509,7 @@ export async function apply(ctx, config) {
507
509
  host: dshMode ? null : cfg.host,
508
510
  pid: state.pid,
509
511
  cwd: state.cwd,
512
+ launchCwd: state.launchCwd,
510
513
  // dsh 模式:同源相对地址(由 DSH webServer 的 prefix 路由提供服务)
511
514
  url: running ? (dshMode ? '/code-server/' : `http://${cfg.host}:${state.port}/`) : null,
512
515
  productPath: state.env?.productPath ?? null,
@@ -617,6 +620,7 @@ export async function apply(ctx, config) {
617
620
  state.status = 'stopped';
618
621
  state.pid = null;
619
622
  state.cwd = null;
623
+ state.launchCwd = null;
620
624
  state.startedAt = null;
621
625
  state.adopted = false;
622
626
  if (reason) state.error = null;
@@ -663,13 +667,29 @@ export async function apply(ctx, config) {
663
667
  return run;
664
668
  }
665
669
 
670
+ /** 已运行实例切换工作区:**只改"当前 workbench 目录",不重启进程**。
671
+ *
672
+ * 为什么够用:工作区目录由客户端 URL 的 `&folder=<绝对路径>` 决定(见 src/factory.js 的
673
+ * buildPageUrl),服务进程自己的 cwd 只影响它 spawn 时对相对路径的解析;真正"换目录"是
674
+ * workbench 拿新 folder 重新导航(客户端导航 iframe)。
675
+ * 旧行为(0.2.11 及以前)在这里 stop('restart') + 重新 launch:切一次工作区就整进程重启,
676
+ * 编辑缓冲、终端、扩展宿主状态全丢,而且侧栏收起时是在后台悄悄发生。
677
+ * 进程 cwd 保持在 launchCwd 里,诊断时能看出"这个实例是哪个目录拉起来的"。 */
678
+ function adoptWorkspace(cwd) {
679
+ const previous = state.cwd;
680
+ state.cwd = cwd;
681
+ console.log(`[code-server] workspace switch: ${previous ?? '(none)'} -> ${cwd}`
682
+ + `(不重启进程,进程 cwd 仍是 ${state.launchCwd ?? '(none)'};workbench 用新的 folder 重新导航)`);
683
+ return snapshot();
684
+ }
685
+
666
686
  async function startInner(cwdArg) {
667
687
  const cwd = typeof cwdArg === 'string' && cwdArg.trim() !== '' ? cwdArg : undefined;
668
688
  if (state.status === 'running' && state.pid !== null) {
669
689
  if (cwd === undefined || cwd === state.cwd) return snapshot();
670
- await stop('restart');
690
+ return adoptWorkspace(cwd);
671
691
  }
672
- // 上一次启动仍在进行:等待它结算后再按本次 cwd 启动,避免 cwd 切换被吞
692
+ // 上一次启动仍在进行:等待它结算后再决定(运行中则同上面一样只换目录,不重启)
673
693
  if (state.status === 'starting') {
674
694
  const deadline = Date.now() + 10000;
675
695
  while (state.status === 'starting' && Date.now() < deadline) {
@@ -683,7 +703,7 @@ export async function apply(ctx, config) {
683
703
  }
684
704
  if (state.status === 'running') {
685
705
  if (cwd === undefined || cwd === state.cwd) return snapshot();
686
- await stop('restart');
706
+ return adoptWorkspace(cwd);
687
707
  }
688
708
  }
689
709
 
@@ -710,6 +730,7 @@ export async function apply(ctx, config) {
710
730
  state.status = 'running';
711
731
  state.pid = record.pid;
712
732
  state.cwd = cwd ?? record.cwd ?? null;
733
+ state.launchCwd = record.launchCwd ?? record.cwd ?? null;
713
734
  state.startedAt = record.startedAt ?? null;
714
735
  state.adopted = true;
715
736
  return snapshot();
@@ -763,6 +784,7 @@ export async function apply(ctx, config) {
763
784
  state.logTail = '';
764
785
  state.status = 'starting';
765
786
  state.cwd = cwd ?? null;
787
+ state.launchCwd = cwd ?? null;
766
788
  state.startedAt = Date.now();
767
789
  state.adopted = false;
768
790
 
@@ -809,6 +831,7 @@ export async function apply(ctx, config) {
809
831
  pid: proc.pid,
810
832
  startedAt: state.startedAt,
811
833
  cwd: cwd ?? null,
834
+ launchCwd: cwd ?? null,
812
835
  host: serve === 'dsh' ? null : cfg.host,
813
836
  port: serve === 'dsh' ? null : cfg.port,
814
837
  serve,
@@ -1080,6 +1103,7 @@ export async function apply(ctx, config) {
1080
1103
  state.status = 'running';
1081
1104
  state.pid = record.pid;
1082
1105
  state.cwd = record.cwd ?? null;
1106
+ state.launchCwd = record.launchCwd ?? record.cwd ?? null;
1083
1107
  state.startedAt = record.startedAt ?? null;
1084
1108
  state.adopted = true;
1085
1109
  console.log(`[code-server] adopted running instance pid=${record.pid} port=${cfg.port}`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-code-server-app",
3
- "version": "0.2.11",
4
- "description": "VS Code (from a code-server release) inside DSH: a right-sidebar tab driven by the plugin's own launcher over the in-process VS Code server (lib/launcher.mjs). The tab claims DSH file addresses (dsh-resource://file/**), so the product's own produced-file chips, delivered-file previews and inline prose mentions open in the workbench. The IDE is a resident surface moved with Element.moveBefore instead of being remounted, so switching sidebar tabs no longer reloads it. Opening the tab switches the right sidebar to fullscreen by default (setting fullscreenOnOpen). Requires a DSH with the right-sidebar services (sidebarRightTabs/sidebarRight, >= 0.1.5-alpha.1); older DSH versions get a single upgrade notice on the settings page and no other UI. Two serving modes: loopback port (default) or same-origin mount on DSH's own webServer (/code-server, protected by ctx.connection.requestRejection). No code-server Node layer, no argon2, no C++ toolchain.",
3
+ "version": "0.2.12",
4
+ "description": "VS Code (from a code-server release) inside DSH: a right-sidebar tab driven by the plugin's own launcher over the in-process VS Code server (lib/launcher.mjs). The tab claims DSH file addresses (dsh-resource://file/**) by file type (setting claimExtensions), so the product's own produced-file chips, delivered-file previews and inline prose mentions open in the workbench. The IDE is a resident surface moved with Element.moveBefore instead of being remounted, so switching sidebar tabs no longer reloads it. Opening the tab switches the right sidebar to fullscreen by default (setting fullscreenOnOpen). Following a workspace switch is lightweight: the workbench re-navigates with the new ?folder= and the IDE process is not restarted (since 0.2.12). Requires a DSH with the right-sidebar services (sidebarRightTabs/sidebarRight, >= 0.1.5-alpha.1); older DSH versions get a single upgrade notice on the settings page and no other UI. Two serving modes: loopback port (default) or same-origin mount on DSH's own webServer (/code-server, protected by ctx.connection.requestRejection). No code-server Node layer, no argon2, no C++ toolchain.",
5
5
  "homepage": "https://github.com/jinsiyu/dsh-code-server-app",
6
6
  "repository": {
7
7
  "type": "git",
@@ -116,6 +116,7 @@
116
116
  "test:apply": "node scripts/test-plugin-apply.mjs",
117
117
  "test:launcher-routes": "node scripts/test-launcher-routes.mjs",
118
118
  "test:fullscreen": "node scripts/test-sidebar-fullscreen.mjs",
119
- "test:claim-types": "node scripts/test-claim-types.mjs"
119
+ "test:claim-types": "node scripts/test-claim-types.mjs",
120
+ "test:workspace-switch": "node scripts/test-workspace-switch.mjs"
120
121
  }
121
122
  }
@@ -3,7 +3,7 @@
3
3
  "vscodeVersion": "1.136.1",
4
4
  "productPath": "stable-8d5f383f301ca20681f5b6606b8207d9dc87bdd8",
5
5
  "layout": "vscode-only",
6
- "preparedAt": "2026-09-11T13:32:44.480Z",
6
+ "preparedAt": "2026-09-11T13:53:46.670Z",
7
7
  "source": "registry",
8
8
  "node": "v24.13.1",
9
9
  "platform": "win32",