dsh-code-server-app 0.3.8 → 0.3.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/README.en.md CHANGED
@@ -207,32 +207,42 @@ only the editor knows, and lets editor gestures drive the current session.
207
207
  still go through its own `fs` tools; the bridge only *knows about* them.
208
208
  - Status bar shows `$(plug) DSH` while connected (click it for the log in the "DSH Editor Bridge" output channel).
209
209
 
210
- ### The three channels
210
+ ### The channels (since 0.3.9 they live on DSH's webServer under `/code-server-bridge`)
211
211
 
212
212
  ```
213
- extension → host POST /api/code-server/bridge/sync one round trip: push editor state + take pending events
214
- extension → host POST /api/code-server/bridge/ask push an editor question into the current session
215
- extension → host GET /api/code-server/bridge/health unauthenticated liveness probe
216
- hostextension POST /api/code-server/bridge/event extension reports open/close etc. (host log tail)
217
- host → extension <extensionsDir>/.dshcs-bridge/bridge.json port + token, re-read by the extension every 5s
213
+ extension → host POST /code-server-bridge/sync one round trip: push editor state + take pending events
214
+ extension → host POST /code-server-bridge/ask push an editor question into the current session
215
+ extension → host GET /code-server-bridge/health unauthenticated liveness probe
216
+ extensionhost POST /code-server-bridge/event extension reports open/close etc. (host log tail)
217
+ host → extension <extensionsDir>/.dshcs-bridge/bridge.json base URL + token, re-read by the extension every 5s
218
218
  ```
219
219
 
220
+ > **Why not under `/api` (fixed in 0.3.9)**: Connection puts a Host/Origin/cookie fence on `/api`
221
+ > (`requestRejection` in `packages/client/connection/src/index.ts` → 401 without a cookie), while the bridge's
222
+ > client is a **Node process inside the extension host** — it can never hold a browser cookie, so its requests were
223
+ > rejected before ever reaching the plugin's route. Measured on 0.3.7: the extension polled
224
+ > `/api/code-server/bridge/sync` and got either 405 (it reached the launcher/VS Code instead) or 401 (the /api
225
+ > fence) — the bridge had never actually synced. It now mounts on DSH's own webServer with its own token as the
226
+ > only gate. The cost: the bridge needs DSH to provide `webServer` — **the web profile has it, desktop does not**.
227
+ > On desktop the host writes no `bridge.json` (dormant beats pointing at a dead address) and says so in the log;
228
+ > **file opening is unaffected** (it uses the signal file and works in every mode).
229
+
220
230
  **Why state is pushed, not pulled**: the extension host is a child process of the VS Code server and **listens on
221
231
  no port** — the host cannot call into it. Editor state therefore rides the extension's own polling request, and
222
232
  the host caches it for the tools (at most one 600 ms cycle behind; older than 10 s and the tool says so instead
223
233
  of passing stale data off as fresh).
224
234
 
225
- **Why no SSE/WebSocket**: `ctx.connection.fetch.register` only allows `GET | HEAD | POST` (streaming would need
226
- the WS mux already owned by `dsh-api-gateway`). Polling also buys two useful properties: it is idempotent (a
227
- dropped event only costs one notification the data always lives in the editor) and the cached state is
228
- inherently fresh.
235
+ **Why no SSE/WebSocket**: the extension host has no HTTP server of its own, and everything DSH can offer is
236
+ request/response (the Connection fetch channel allows only `GET | HEAD | POST`; streaming would need the WS mux
237
+ already owned by `dsh-api-gateway`). Polling also buys two useful properties: it is idempotent (a dropped event
238
+ only costs one notification — the data always lives in the editor) and the cached state is inherently fresh.
229
239
 
230
240
  ### Security model (four invariants; read before touching `lib/bridge.mjs`)
231
241
 
232
242
  The token lives in `<extensionsDir>/.dshcs-bridge/bridge.json`, **readable by any process of the same local
233
243
  user**, so:
234
244
 
235
- 1. **`/api/code-server/bridge/*` is permanently read-only.** No route writes files, edits documents, or runs
245
+ 1. **`/code-server-bridge/*` is permanently read-only.** No route writes files, edits documents, or runs
236
246
  commands. A leaked token is therefore bounded to "sees information that is in the editor" and **can never**
237
247
  become arbitrary file writes or command execution. A whitelist assertion in `scripts/test-bridge-routes.mjs`
238
248
  guards this.
@@ -574,10 +584,10 @@ Host/Origin fence and browser auth); in the desktop profile `apps/desktop-host`
574
584
  | POST | `/api/code-server/stop` | Stop and recycle the process tree |
575
585
  | 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 |
576
586
  | 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 |
577
- | GET | `/api/code-server/bridge/health` | **unauthenticated**: `{ ok, bridge, pid, url }` — liveness only, no editor data |
578
- | POST | `/api/code-server/bridge/sync` | editor bridge: the extension pushes state (`{context, diagnostics, workspace, at}`) and takes back events; `?since=<seq>` is the event cursor. Requires `x-dshcs-bridge-token` |
579
- | POST | `/api/code-server/bridge/ask` | editor bridge: push an editor question into the current session (`{text, file?, lineStart?, lineEnd?, selection?, languageId?}`); **409** when no session can receive it |
580
- | POST | `/api/code-server/bridge/event` | editor bridge: extension reports open/close and similar (host log tail). Requires the token |
587
+ | GET | `/code-server-bridge/health` | editor-bridge liveness (**unauthenticated**; no editor data). Mounted on DSH's webServer, not under `/api` |
588
+ | POST | `/code-server-bridge/sync` | editor bridge: the extension pushes state (`{context, diagnostics, workspace, at}`) and takes back events; `?since=<seq>` is the event cursor. Requires `x-dshcs-bridge-token`, and **any Origin header is 403** |
589
+ | POST | `/code-server-bridge/ask` | editor bridge: push an editor question into the current session (`{text, file?, lineStart?, lineEnd?, selection?, languageId?}`); **409** when no session can receive it |
590
+ | POST | `/code-server-bridge/event` | editor bridge: extension reports open/close and similar (host log tail). Requires the token |
581
591
 
582
592
  > All four bridge routes carry their own token check — they **cannot** rely on DSH's cookie fence, because the
583
593
  > extension host has no browser cookie — and they are read-only by construction. See "Working with DSH" above.
@@ -653,9 +663,13 @@ What remains on the plugin side:
653
663
 
654
664
  ## Known limitations
655
665
 
656
- - **The editor bridge only works under `serve: loopback`.** `serve: dsh` is a named-pipe mode with no dedicated
657
- port, so the bridge's "loopback host + own token" model does not apply: it stays disabled there
658
- (`bridge.supported=false` in `status`). Use the default `loopback` when you want the integration.
666
+ - **The editor bridge needs DSH to provide `webServer`** (corrected in 0.3.9): its client is a Node process inside
667
+ the extension host, which can only reach DSH over HTTP at DSH's own origin (the bridge mounts under
668
+ `BRIDGE_BASE` with its own token). **The web profile has webServer (both `serve: dsh` and loopback) the bridge
669
+ works; desktop has none → it stays disabled** (`bridge.supported=false`, no `bridge.json` is written, one log
670
+ line explains it). **File opening is unaffected**: it uses the signal file and works regardless of mode.
671
+ Up to 0.3.7 the bridge was registered under `/api/code-server/bridge/*` and was killed by Connection's cookie
672
+ fence (401) — that was a bug.
659
673
  - **Bridged state can lag by up to 600 ms**, and the tools say "stale" rather than serving data older than 10 s.
660
674
  - **Unsaved buffers are reported, not taken over.** The agent still edits via its own `fs` tools, i.e. against
661
675
  disk. What the bridge adds is a notice *before* writing a dirty file, a diff *after*, and a warning instead of
package/README.md CHANGED
@@ -203,34 +203,47 @@ DSH 用**资源地址**命名文件,`openFile` 只负责把地址交给右侧栏
203
203
  - 编辑器侧的入口还有状态栏的 `$(plug) DSH`(连通时显示,点击打开日志),日志在输出面板
204
204
  「DSH Editor Bridge」里 —— 出问题时先看它。
205
205
 
206
- ### 三条通道
206
+ ### 三条通道(0.3.9 起走 DSH webServer 的 `/code-server-bridge`)
207
207
 
208
208
  ```
209
- 扩展 → host POST /api/code-server/bridge/sync 一趟来回:上报编辑器状态 + 取回待处理事件
210
- 扩展 → host POST /api/code-server/bridge/ask 把编辑器里的提问投进当前会话
211
- 扩展 → host GET /api/code-server/bridge/health 无鉴权探活(便于重启后一眼确认)
212
- host 扩展 POST /api/code-server/bridge/event 扩展上报打开/关闭文件等(进 host 日志尾)
213
- host → 扩展 <extensionsDir>/.dshcs-bridge/bridge.json 端口 + 令牌(扩展每 5s 重读)
209
+ 扩展 → host POST /code-server-bridge/sync 一趟来回:上报编辑器状态 + 取回待处理事件
210
+ 扩展 → host POST /code-server-bridge/ask 把编辑器里的提问投进当前会话
211
+ 扩展 → host GET /code-server-bridge/health 无鉴权探活(便于重启后一眼确认)
212
+ 扩展 host POST /code-server-bridge/event 扩展上报打开/关闭文件等(进 host 日志尾)
213
+ host → 扩展 <extensionsDir>/.dshcs-bridge/bridge.json base URL + 令牌(扩展每 5s 重读)
214
214
  ```
215
215
 
216
+ > **为什么不在 `/api` 下(0.3.9 修正)**:Connection 给 `/api` 装了 Host/Origin/cookie fence
217
+ > (`packages/client/connection/src/index.ts` 里 `requestRejection` → 无 cookie 即 401),而桥的客户端
218
+ > 是扩展宿主里的 **Node 进程** —— 它永远拿不到浏览器 cookie,请求在到达插件路由之前就被挡掉了。
219
+ > 实测(0.3.7):扩展按 `/api/code-server/bridge/sync` 轮询,要么 405(打到 launcher/VS Code)、
220
+ > 要么 401(打到 DSH 的 /api fence),**桥从来没有真正同步过**。
221
+ > 现在桥挂在 DSH 自己的 webServer 前缀下,鉴权完全由桥自己的令牌承担(见下)。
222
+ > 代价:桥需要 DSH 提供 `webServer` —— **web profile 有,desktop 没有**。
223
+ > desktop 下 host 不写 `bridge.json`(宁可休眠,不可指向死地址),并在日志里说明;
224
+ > **文件打开不受影响**(它走信号文件,与 serve 模式无关)。
225
+
216
226
  **为什么状态是"推"而不是"拉"**:扩展宿主是 VS Code server 的一个子进程,**不监听任何端口** ——
217
227
  host 反向请求不到它。所以编辑器状态只能在扩展主动发起的那趟轮询里带上来,host 缓存后给工具读
218
228
  (缓存滞后最多一个轮询周期 600ms,超过 10s 没更新就判为过期,工具会明说"状态已过期");
219
229
 
220
- **为什么不用 SSE/WebSocket**:`ctx.connection.fetch.register` methods 只允许 `GET | HEAD | POST`
221
- (流式要另走已被 `dsh-api-gateway` 占用的 WS mux)。轮询反而给了两条好性质:幂等(丢一次事件只是
222
- 少一次提示,数据本身永远在编辑器里),以及状态天然最新(每趟都刷新)。
230
+ **为什么不用 SSE/WebSocket**:扩展宿主里没有 HTTP 服务器,而 DSH 侧能给的无非是请求/响应
231
+ (Connection 的 fetch 通道只允许 `GET | HEAD | POST`,流式要另走已被 `dsh-api-gateway` 占用的 WS mux)
232
+ 轮询反而给了两条好性质:幂等(丢一次事件只是少一次提示,数据本身永远在编辑器里),以及状态天然最新(每趟都刷新)。
223
233
 
224
234
  ### 安全模型(四条不变量,改 `lib/bridge.mjs` 之前先读)
225
235
 
226
236
  桥的令牌写在 `<extensionsDir>/.dshcs-bridge/bridge.json`(**对本机同用户进程可读**),所以:
227
237
 
228
- 1. **`/api/code-server/bridge/*` 永久只读。** 没有写文件、改文档、执行命令的路由。
238
+ 1. **`/code-server-bridge/*` 永久只读。** 没有写文件、改文档、执行命令的路由。
229
239
  令牌泄露的爆炸半径被封在"看到编辑器里的信息",**不会**变成任意文件写/任意命令执行。
230
- `scripts/test-bridge-routes.mjs` 里有一条白名单断言盯着这件事。
240
+ `scripts/test-bridge-routes.mjs` 里有一条白名单断言盯着这件事(未知后缀一律 404)。
231
241
  2. **带 `Origin` 的请求一律 403。** 浏览器发起必带 Origin(含沙箱 iframe 的 `Origin: null`),
232
242
  扩展宿主是 Node 进程、不带。判定顺序上 Origin **先于令牌** —— 否则等于给浏览器一个
233
243
  "令牌猜对没有"的 oracle。
244
+ 实现细节:Node 路由 → Fetch 适配器把**原始 headers** 挂在 request 上(`dshcsRawHeaders`),
245
+ 因为 undici 的 `Request` 构造器会把 `origin` 当 forbidden header 归一化掉 —— 读 `request.headers`
246
+ 会让这道 403 静默失效(测试里有这条实测记录)。
234
247
  3. **路径收敛在编辑器当前工作区**(`workspaceFolder` 之外的诊断直接丢弃)。
235
248
  4. **有界**:诊断默认 200 条 / 单条截断 500 字符 / 上报体上限 256KB / 事件环形缓冲 64 条。
236
249
 
@@ -574,15 +587,15 @@ desktop profile 由 `apps/desktop-host` 把 `/api/*` 交给同一个 `createShar
574
587
  | POST | `/api/code-server/stop` | 停止并回收进程树 |
575
588
  | POST | `/api/code-server/setup` | **兼容空操作**:0.1.36 起依赖由包管理器安装,调用只重新自检 `env` 并返回 |
576
589
  | POST | `/api/code-server/open-file` | body `{ file }` — 写信号文件,由内置扩展 `dshcs-open-file` 在 code-server 中打开 |
577
- | GET | `/api/code-server/bridge/health` | **无鉴权**:`{ ok, bridge, pid, url }`。只回答"桥活着吗",不含任何编辑器数据 |
578
- | POST | `/api/code-server/bridge/sync` | 编辑器桥:扩展上报状态(`{context, diagnostics, workspace, at}`)并取回事件;`?since=<seq>` 是事件游标。需 `x-dshcs-bridge-token` |
579
- | POST | `/api/code-server/bridge/ask` | 编辑器桥:把编辑器里的提问投进当前会话(`{text, file?, lineStart?, lineEnd?, selection?, languageId?}`);没有可投递的会话时回 **409** |
580
- | POST | `/api/code-server/bridge/event` | 编辑器桥:扩展上报打开/关闭文件等(进 host 日志尾)。需令牌 |
590
+ | GET | `/code-server-bridge/health` | 编辑器桥探活(**无鉴权**;只回答"桥活着吗",不含任何编辑器数据)。挂 DSH webServer,不在 `/api` |
591
+ | POST | `/code-server-bridge/sync` | 编辑器桥:扩展上报状态(`{context, diagnostics, workspace, at}`)并取回事件;`?since=<seq>` 是事件游标。需 `x-dshcs-bridge-token`,**带 Origin 一律 403** |
592
+ | POST | `/code-server-bridge/ask` | 编辑器桥:把编辑器里的提问投进当前会话(`{text, file?, lineStart?, lineEnd?, selection?, languageId?}`);没有可投递的会话时回 **409** |
593
+ | POST | `/code-server-bridge/event` | 编辑器桥:扩展上报打开/关闭文件等(进 host 日志尾)。需令牌 |
581
594
 
582
595
  > 桥的四条路由都自带令牌鉴权(它们**不依赖** DSH 的 cookie fence —— 扩展宿主拿不到浏览器 cookie),
583
- > 且永远只读。前面的守护规则与取舍见「与 DSH 的协同」。
596
+ > 且永远只读。这也是它们**不能**挂在 `/api` 下的原因(见「与 DSH 的协同」)。
584
597
 
585
- > 插件不再注册 `/code-server/*` 这类 webServer 专有路由;code-server 图标已内联为 data URI(client bundle 内),
598
+ > 除桥之外,插件不再注册任何插件自有 HTTP 路由;code-server 图标已内联为 data URI(client bundle 内),
586
599
  > 因此客户端不请求任何插件自有 HTTP 资源。
587
600
 
588
601
  ## DSH Desktop(无 webServer)
@@ -632,9 +645,12 @@ desktop profile 由 `apps/desktop-host` 把 `/api/*` 交给同一个 `createShar
632
645
 
633
646
  ## 已知限制
634
647
 
635
- - **编辑器桥只在 `serve: loopback` 下工作**:`serve: dsh` 是管道模式、没有独立端口,桥的
636
- "回环 Host + 独立令牌"模型不适用 该模式下自动不启用(status `bridge.supported=false`)。
637
- 需要协同能力就用默认的 `loopback`(0.2.0 起也是默认)
648
+ - **编辑器桥需要 DSH 提供 `webServer`**(0.3.9 修正):桥的客户端是扩展宿主里的 Node 进程,
649
+ 它只能通过 HTTP 打到 DSH 自己的 origin(桥挂在 `BRIDGE_BASE` 前缀下,自带令牌鉴权)。
650
+ **web profile(`serve: dsh` 或 loopback 都行)有 webServer → 桥可用;desktop 没有 → 不启用**
651
+ (status 的 `bridge.supported=false`,host 不写 `bridge.json`,日志里说明一次)。
652
+ **文件打开不受影响**:它走信号文件,与 serve 模式和 webServer 都无关。
653
+ 0.3.7 及以前把桥挂在 `/api/code-server/bridge/*`,被 Connection 的 cookie fence 401 挡死 —— 那是个 bug。
638
654
  - **桥的状态有最多 600ms 滞后**:扩展每 600ms 推一次;超过 10s 没更新时工具会明说"状态已过期"
639
655
  而不是拿旧数据当新数据(例如用户在 IDE 里关掉面板之后)。
640
656
  - **未保存缓冲区是"上报"而不是"接管"**:agent 仍然通过它自己的 `fs` 工具按磁盘内容编辑。
@@ -499,7 +499,7 @@ function activate(context) {
499
499
  // 实际方向是:本扩展在每次轮询里 `POST <BRIDGE_BASE>/sync`,把状态推上去、
500
500
  // 同时取回 host 的待处理事件(agent 改了哪个文件)。host 侧缓存状态供 agent 工具读。
501
501
  // 见 lib/bridge-client.js 的 sync() 与 lib/bridge.mjs 顶部的通道说明。
502
- // **路径是 /code-server-bridge,不是 /api/...**(0.3.8 修正):/api 那层要求浏览器 cookie,
502
+ // **路径是 /code-server-bridge,不是 /api/...**(0.3.9 修正):/api 那层要求浏览器 cookie,
503
503
  // 扩展宿主拿不到 ⇒ 请求永远到不了插件路由。
504
504
 
505
505
  // 命令
@@ -23,7 +23,7 @@ const path = require('path');
23
23
  const BRIDGE_DIRNAME = '.dshcs-bridge';
24
24
  const BRIDGE_FILENAME = 'bridge.json';
25
25
  /** 桥的挂载前缀。**故意不在 `/api` 下**:那层有 Connection 的 cookie fence,扩展宿主(Node 进程)
26
- * 拿不到浏览器 cookie,请求会在到达插件路由之前被 401(0.3.8 修正)。 */
26
+ * 拿不到浏览器 cookie,请求会在到达插件路由之前被 401(0.3.9 修正)。 */
27
27
  const BRIDGE_BASE = '/code-server-bridge';
28
28
  const TOKEN_HEADER = 'x-dshcs-bridge-token';
29
29
  const STATE_FILENAME = 'extension-state.json';
package/lib/bridge.mjs CHANGED
@@ -51,7 +51,7 @@ export const BRIDGE_FILENAME = 'bridge.json';
51
51
 
52
52
  /** 桥路由前缀。
53
53
  *
54
- * **故意不放在 `/api` 下(0.3.8 修正)**:Connection 给 `/api` 装了 Host/Origin/cookie fence
54
+ * **故意不放在 `/api` 下(0.3.9 修正)**:Connection 给 `/api` 装了 Host/Origin/cookie fence
55
55
  * (`packages/client/connection/src/index.ts`:`requestRejection` → 无 cookie 即 401),而桥的客户端是
56
56
  * VS Code 扩展宿主里的一个 Node 进程 —— 它**永远拿不到浏览器 cookie**,请求在到达插件路由之前就被挡掉了。
57
57
  * 实测(0.3.7):扩展按 `/api/code-server/bridge/sync` 轮询,请求要么 405(打到 launcher/VS Code)、
package/lib/index.js CHANGED
@@ -640,7 +640,7 @@ export async function apply(ctx, config) {
640
640
 
641
641
  /** 本次启动生成的新桥令牌;null = 本实例没有令牌(adopt 旧实例时会回读 bridge.json)。 */
642
642
  let bridgeToken = null;
643
- /** 桥的 webServer 挂载点(0.3.8)。null = 本部署没有可挂载的 HTTP 面(desktop)。 */
643
+ /** 桥的 webServer 挂载点(0.3.9)。null = 本部署没有可挂载的 HTTP 面(desktop)。 */
644
644
  let bridgeMountDispose = null;
645
645
  /** "桥不可用"只提示一次,避免每次起停都刷屏。 */
646
646
  let bridgeUnavailableLogged = false;
@@ -854,7 +854,7 @@ export async function apply(ctx, config) {
854
854
  dshMount = null;
855
855
  console.error(`[code-server] serve=dsh 挂载失败(将回退 loopback):${error && error.message ? error.message : error}`);
856
856
  }
857
- // 编辑器桥(0.3.8):挂在自己的前缀上,自带令牌 —— 走 /api 会被 Connection 的 cookie fence 401
857
+ // 编辑器桥(0.3.9):挂在自己的前缀上,自带令牌 —— 走 /api 会被 Connection 的 cookie fence 401
858
858
  // (扩展宿主是 Node 进程,没有浏览器 cookie),详见 bridgeOrigin() 的注释。
859
859
  try {
860
860
  bridgeMountDispose = wsCtx.webServer.register({
@@ -1566,7 +1566,7 @@ export async function apply(ctx, config) {
1566
1566
  { path: `${API_BASE}/setup`, methods: ['POST'], fetch: handleSetup },
1567
1567
  { path: `${API_BASE}/open-file`, methods: ['POST'], fetch: handleOpenFile },
1568
1568
  { path: `${API_BASE}/ui-mode`, methods: ['POST'], fetch: handleUiMode },
1569
- // ---- 编辑器桥的 4 条路由**不在 /api 下**(0.3.8 修正)----
1569
+ // ---- 编辑器桥的 4 条路由**不在 /api 下**(0.3.9 修正)----
1570
1570
  // 原因:Connection 给 `/api` 装了 cookie fence(无 cookie → 401),而扩展宿主是 Node 进程、
1571
1571
  // 永远拿不到浏览器 cookie ⇒ 挂在这里的路由根本到不了(实测:带令牌也被 401/405 挡回)。
1572
1572
  // 现在挂到 DSH webServer 的 `${BRIDGE_BASE}/*`,自带桥令牌鉴权(见该处注释与 lib/bridge.mjs 的安全不变量)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-code-server-app",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
4
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). Since 0.3.0 the bundle also ships an editor bridge (assets/extensions/dshcs-editor-bridge): a read-only channel between the in-tree VS Code extension host and DSH, giving the agent what only the editor knows (unsaved buffers, language-server diagnostics, the active selection) and letting editor gestures drive the session. 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": {
@@ -3,7 +3,7 @@
3
3
  "vscodeVersion": "1.137.0",
4
4
  "productPath": "stable-b11dabdaca0d3369986975be285db92c8795cea5",
5
5
  "layout": "vscode-only",
6
- "preparedAt": "2026-09-12T16:19:04.694Z",
6
+ "preparedAt": "2026-09-12T16:34:30.093Z",
7
7
  "source": "registry",
8
8
  "node": "v24.21.0",
9
9
  "platform": "win32",