dsh-code-server-app 0.2.13 → 0.2.14

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
@@ -47,7 +47,9 @@ A static profile plugin (npm package with host + client bundle) that ships the *
47
47
  Open the IDE from the **Code Server box** on the sidebar's guide page, or by clicking DSH's own produced-file chips / delivered-file previews / inline file names;
48
48
  diagnostics stay out of the UI — the `[code-server]` lines in the DSH host log are the place to look (`/api/code-server/status` still returns `env` for scripts).
49
49
  The old `windowedOpen` (open in a window) and `reserveComposer` were **removed in 0.2.6**: leftover keys in an old settings document neither fail nor apply (they are no longer part of the schema).
50
- To use the IDE in a browser tab, visit the loopback address `http://127.0.0.1:<port>/` (or DSH's `/code-server/` under `serve: dsh`).
50
+ To use the IDE in a browser tab, **copy the full address** from the settings card / empty-state hint (it contains the
51
+ path token: `http://127.0.0.1:<port>/<token>/`; under `serve: dsh` it is DSH's `/code-server/`) — dropping the token
52
+ segment yields a 404.
51
53
 
52
54
  ## Opening files (official entry points since 0.2.5)
53
55
 
@@ -143,9 +145,36 @@ state preserved (no full reload). Full evidence and probe scripts: `docs/analysi
143
145
 
144
146
  | Mode | What it does | Requires |
145
147
  |---|---|---|
146
- | **`loopback` (default)** | the plugin listens on its own loopback port (`host:port`) and the sidebar iframe connects cross-origin; the process can be adopted after a DSH host restart | nothing |
148
+ | **`loopback` (default)** | the plugin listens on its own loopback port (**`port: 0` by default = a random port assigned per start**), the sidebar iframe connects cross-origin, and the URL carries a **random path token** (`http://127.0.0.1:<port>/<token>/`, see "Security model of the loopback port" below); the process can be adopted after a DSH host restart | nothing |
147
149
  | **`dsh`** | the IDE is mounted on **DSH's own HTTP port** at `/code-server/*` (HTTP prefix route) plus `/code-server/<quality>-<commit>` (exact WebSocket route), forwarded to the launcher's **named pipe**; **no extra port**; every request (including the WS handshake) first passes `ctx.connection.requestRejection()` — the same Host/Origin fence and browser-cookie authentication as `/api` | DSH providing `webServer` (web profile); desktop falls back to loopback automatically |
148
150
 
151
+ ### Security model of the loopback port (since 0.2.14)
152
+
153
+ `loopback` is the only transport desktop has (no webServer, no same-origin mount), so it is hardened on its own:
154
+
155
+ - **Random port**: `port` defaults to `0` → the OS assigns a free port and the launcher writes the **actual** one to
156
+ `$DSH_HOME/code-server/endpoint.json`, which the host reads back. The port therefore changes on every start and the old
157
+ "8090 is busy" class of conflicts is gone. Pin `port` explicitly if you need a fixed address.
158
+ - **Path token**: a fresh 32-character token (`[0-9A-Za-z_-]`, 24 random bytes) is generated on every **new start**, stored in
159
+ `$DSH_HOME/code-server/path-token` (inside the user profile, readable only by the owner under the default ACL), and becomes
160
+ the URL path prefix. Requests without that prefix get a plain **404** (nothing reveals that an IDE lives there); a prefix
161
+ without the trailing slash is answered with a 302.
162
+ - **Why not VS Code's own `connection-token`**: it works through `?tkn=` → 302 + `Set-Cookie: vscode-tkn; SameSite=Lax`.
163
+ The desktop iframe is **cross-origin** (`dsh-app://` → `127.0.0.1`), and a Lax cookie is not sent from a cross-site
164
+ subframe — the IDE would simply fail to load. A path prefix needs no cookie at all: the workbench derives every asset and
165
+ WebSocket URL from `location.pathname` (the same mechanism already proven by mounting under `/code-server/` in `serve: dsh`),
166
+ so the prefix rides along on every subrequest and on the WS handshake. (Verified with a real Edge + CDP run: with a random
167
+ port and a token, the workbench renders **inside a cross-origin iframe** and establishes its WebSocket.)
168
+ - **Host allowlist**: in loopback mode only `127.0.0.1 | localhost | [::1] : <actual port>` is accepted. This is what stops
169
+ DNS rebinding, whose requests can arrive without an `Origin` header and therefore slip past the `Origin == Host` check.
170
+ - **`Referrer-Policy: no-referrer`**: the token lives in the path, so it must not leak through `Referer` when external resources load.
171
+ - **The token never reaches argv or the logs**: command lines are readable by any local process, so it travels through a file;
172
+ the log only says "enabled".
173
+
174
+ Boundary, stated plainly: this layer stops other local applications, port scanners and browser pages from casually reaching
175
+ your IDE. A **malicious program running as the same user** can already read your files and that token file — that is outside
176
+ this plugin's threat model.
177
+
149
178
  - Switch it in `config.serve` in `cordis.patch.yml` or in Settings → Plugins → Code Server (takes effect on the next start).
150
179
  - Benefits of `dsh`: a single URL/port (remote access to DSH gives you the IDE), no extra loopback listener, authentication on par with DSH.
151
180
  - Two **known trade-offs** of `dsh`: the iframe shares DSH's origin, so `sandbox` is dropped there (same-origin plus
@@ -444,7 +473,7 @@ reports the tree version / `productPath` / server entry, VS Code inner dependenc
444
473
  |---|---|---|
445
474
  | `bin` | `code-server` (placeholder) | Launch priority: explicit `bin` in config > the tree package `@jinsiyu/dshcs-code-server/code-server/out/node/entry.js` > the old platform sub-packages `@jinsiyu/dshcs-code-server-<platform>-<arch>` > the in-package `vendor/code-server` > the old install root `.code-server-app` > plugin-internal `node_modules` > `code-server` on PATH. None present → startup error with troubleshooting hints |
446
475
  | `host` | `127.0.0.1` | Bind address; `auth: none` only allows loopback (localhost/127.0.0.1/::1) |
447
- | `port` | `8090` | Port; on conflict startup fails with diagnostics (no automatic port change) |
476
+ | `port` | `0` | Port for loopback mode; **`0` = a random free port assigned per start** (the actual one is written to `endpoint.json` and read back by the host). Give an explicit port to pin it; when that port is taken and no valid `pid.json` exists, startup fails with diagnostics instead of killing a stranger |
448
477
  | `auth` | `none` | `none` \| `password`; non-loopback host automatically requires password |
449
478
  | `passwordToken` | `''` | Token for password mode (passed to code-server via the `PASSWORD` env var) |
450
479
  | `userDataDir` | `$DSH_HOME/code-server/user-data` | User-data isolation directory |
@@ -568,4 +597,8 @@ What remains on the plugin side:
568
597
  switching tabs or collapsing the sidebar and back **does not reload** it. Browsers without `moveBefore` fall back to
569
598
  the old behaviour (`appendChild` → full reload), reported as `degraded`; see "Why switching tabs no longer reloads".
570
599
  - **Remote access**: with `serve: dsh` the browser only needs to reach DSH itself (one port, protected exactly like `/api`);
571
- `serve: loopback` stays loopback-only with `auth: none`, and 0.2.0 no longer supports `auth: password`.
600
+ `serve: loopback` binds to loopback only (random port + path token + Host allowlist see "Security model of the loopback
601
+ port"), so use `serve: dsh` for cross-machine access (0.2.0 no longer supports `auth: password`).
602
+ - **The loopback token rotates per instance**: port and token change on every new start; adoption after a host restart
603
+ matches the live instance through the `endpoint.json` and `path-token` files, so **do not delete those two files**
604
+ (without them the host cannot recognise the old instance and treats the port as foreign).
package/README.md CHANGED
@@ -61,7 +61,8 @@
61
61
  打开 IDE 用右侧栏「开始」页的 **Code Server 入口框**,或直接点官方的产物 chip / 交付卡片 / 正文文件名;
62
62
  诊断看 DSH host 日志里的 `[code-server]` 输出(`/api/code-server/status` 仍返回 `env` 供脚本排查)。
63
63
  旧版的 `windowedOpen`(窗口化打开)、`reserveComposer` 已在 0.2.6 移除:旧设置文档里残留的这两个键不会报错,只是被忽略(不再出现在 schema 里)。
64
- 需要在新标签页用 IDE 时,直接访问回环地址 `http://127.0.0.1:<port>/`(`serve: dsh` 时为 DSH 的 `/code-server/`)。
64
+ 需要在新标签页用 IDE 时,从设置卡/空态提示里**复制完整地址**(含路径令牌,`http://127.0.0.1:<port>/<token>/`;
65
+ `serve: dsh` 时为 DSH 的 `/code-server/`)—— 少了令牌那一段会 404。
65
66
 
66
67
  ## 文件打开(0.2.5 起走官方入口)
67
68
 
@@ -146,9 +147,31 @@ DSH 用**资源地址**命名文件,`openFile` 只负责把地址交给右侧栏
146
147
 
147
148
  | 方式 | 说明 | 需要 |
148
149
  |---|---|---|
149
- | **`loopback`(默认)** | 插件自己起一个回环端口(`host:port`),右侧栏 iframe 跨源直连;进程可被 adopt(DSH host 重启后接管) | 无 |
150
+ | **`loopback`(默认)** | 插件自己起一个回环端口(**默认 `port: 0` = 每次启动由系统分配随机端口**),右侧栏 iframe 跨源直连;**URL 带随机路径令牌**(`http://127.0.0.1:<port>/<token>/`,见下「回环端口的安全模型」);进程可被 adopt(DSH host 重启后接管) | 无 |
150
151
  | **`dsh`** | IDE 挂到 **DSH 自己的 HTTP 端口**上的 `/code-server/*`(HTTP prefix 路由)+ `/code-server/<quality>-<commit>`(WS 精确路由),转发到 launcher 的**命名管道**;**没有额外端口**;每条请求(含 WS 握手)先过 `ctx.connection.requestRejection()` —— 与 `/api` 同一套 Host/Origin fence + 浏览器 cookie 认证 | DSH 提供 `webServer` 服务(web profile);desktop 无此服务 → 自动回退 loopback |
151
152
 
153
+ ### 回环端口的安全模型(0.2.14 起)
154
+
155
+ `loopback` 是 desktop 端唯一的通路(无 webServer、无同源挂载),所以它单独加固了一层:
156
+
157
+ - **随机端口**:`port` 默认 `0` → 由系统分配空闲端口,launcher 把**实际**端口写进 `$DSH_HOME/code-server/endpoint.json`,
158
+ host 读回(因此端口每次都变、也不存在"8090 被占用"这类冲突)。要固定地址就显式配 `port`。
159
+ - **路径令牌**:每次**新启动**生成 32 位随机令牌(`[0-9A-Za-z_-]`,24 字节随机),写在
160
+ `$DSH_HOME/code-server/path-token`(用户 profile 下,默认 ACL 仅本人可读),成为 URL 的路径前缀。
161
+ 没有这个前缀的请求一律 **404**(不泄露"这里跑着 IDE"),前缀不带结尾斜杠会 302 补上。
162
+ - **为什么不用 VS Code 自带的 `connection-token`**:它靠 `?tkn=` → 302 + `Set-Cookie: vscode-tkn; SameSite=Lax`;
163
+ 而 desktop 的 iframe 是**跨源**的(`dsh-app://` → `127.0.0.1`),Lax cookie 在跨站子框架里不会被带上
164
+ → 会让 IDE 直接打不开。路径前缀不需要 cookie:workbench 的资源与 WS 全部由 `location.pathname` 派生
165
+ (`serve: dsh` 挂在 `/code-server/` 下已验证同一机制),前缀天然跟随每个子请求与 WS 握手。
166
+ (已用真实 Edge + CDP 在**跨源 iframe** 里验证:随机端口 + 令牌下 workbench 正常渲染并建立 WS。)
167
+ - **Host 白名单**:回环模式只接受 `127.0.0.1 | localhost | [::1] : <实际端口>`。挡的是 DNS rebinding ——
168
+ 这类攻击构造的请求可以不带 `Origin`,只靠 `Origin == Host` 那条检查拦不住。
169
+ - **`Referrer-Policy: no-referrer`**:令牌在路径里,不能让它在加载站外资源时经 `Referer` 漏出去。
170
+ - **令牌不落 argv、不进日志**:命令行对本机任意进程可见,所以走文件传递;日志里只打印"已启用"。
171
+
172
+ 边界(说清楚,不夸大):这一层挡的是"本机其它应用/端口扫描器/浏览器页面"顺手访问你的 IDE;
173
+ **同用户的本地恶意程序**本来就能直接读你的文件、也能读那个令牌文件 —— 那不在本插件的威胁模型内。
174
+
152
175
  - 在 `cordis.patch.yml` 的 `config.serve`(或设置文档里的 `code-server.serve`)切换,下次启动生效 —— **设置卡片不提供这一行**(卡片只有认领类型/打开即全屏/后台常驻三个设置)。
153
176
  - `dsh` 模式的实际收益:单一 URL/单一端口(远程访问 DSH 即可用 IDE)、不再暴露额外回环端口、认证与 DSH 同级。
154
177
  - `dsh` 模式的两点**已知取舍**:
@@ -441,8 +464,8 @@ host 探测顺序:`@jinsiyu/dshcs-vscode-server/vscode`(**0.2.0+ 正式布局**)
441
464
  | `serve` | `loopback` | 服务方式:`loopback`(独立回环端口,iframe 跨源)→ `dsh`(挂到 DSH 自身端口的 `/code-server/*`,转发到命名管道,复用 DSH 的 Host/Origin + cookie 防护)。需 DSH 提供 `webServer`,缺失时自动回退 loopback |
442
465
  | `bin` | `''`(空 = 用自带 launcher) | 逃生舱:显式指定外部 code-server 可执行文件 / `out/node/entry.js` 时退回旧模型(不经 `lib/launcher.mjs`) |
443
466
  | `host` | `127.0.0.1` | loopback 模式的绑定地址(仅允许回环) |
444
- | `port` | `8090` | loopback 模式的端口;被占用时启动失败并给出诊断(不自动换端口) |
445
- | `auth` | `none` | 固定 `none`(0.2.0 起 argon2 已移除;需要对外访问请用 `serve: dsh`) |
467
+ | `port` | `0` | loopback 模式的端口;**`0` = 每次启动由系统分配随机端口**(实际端口写在 `endpoint.json`,host 读回)。显式给端口则固定使用;该端口被占用且无有效 `pid.json` 时报错并给诊断(拒绝误杀) |
468
+ | `auth` | `none` | 固定 `none`(0.2.0 起 argon2 已移除);回环模式的访问控制由**随机端口 + 路径令牌 + Host 白名单**承担(见「回环端口的安全模型」),对外访问请用 `serve: dsh` |
446
469
  | `userDataDir` | `$DSH_HOME/code-server/user-data` | 用户数据隔离目录 |
447
470
  | `extensionsDir` | `$DSH_HOME/code-server/extensions` | 扩展目录 |
448
471
  | `locale` | `''` | 界面语言(空 = 跟随浏览器),如 `zh-cn` |
@@ -466,8 +489,8 @@ desktop profile 由 `apps/desktop-host` 把 `/api/*` 交给同一个 `createShar
466
489
 
467
490
  | 方法 | 路径 | 说明 |
468
491
  |---|---|---|
469
- | GET | `/api/code-server/status` | `{ ok, running, status, host, port, pid, cwd, launchCwd, url, version, error, logTail, adopted }`(另含 `env` 环境检测与 `setup` 兼容字段;`cwd` = 当前 workbench 目录,`launchCwd` = 进程启动目录) |
470
- | POST | `/api/code-server/start` | body `{ cwd? }`(省略 cwd 不切换工作目录);幂等;运行中切 cwd = **只换目录不重启进程**(0.2.12) |
492
+ | GET | `/api/code-server/status` | `{ ok, running, status, host, port, pid, cwd, launchCwd, url, version, error, logTail, adopted }`(另含 `env` 环境检测与 `setup` 兼容字段;`cwd` = 当前 workbench 目录,`launchCwd` = 进程启动目录,loopback 下 `port` = **实际**端口、`url` = 含路径令牌的完整地址) |
493
+ | POST | `/api/code-server/start` | body `{ cwd? }`(省略 cwd 不切换工作目录);幂等;运行中切 cwd = **只换目录不重启进程**(0.2.12);loopback 新启动会**轮换端口与路径令牌**(0.2.14) |
471
494
  | POST | `/api/code-server/stop` | 停止并回收进程树 |
472
495
  | POST | `/api/code-server/setup` | **兼容空操作**:0.1.36 起依赖由包管理器安装,调用只重新自检 `env` 并返回 |
473
496
  | POST | `/api/code-server/open-file` | body `{ file }` — 写信号文件,由内置扩展 `dshcs-open-file` 在 code-server 中打开 |
@@ -539,5 +562,8 @@ desktop profile 由 `apps/desktop-host` 把 `/api/*` 交给同一个 `createShar
539
562
  切标签/收起侧栏再回来**不重载**。不支持 `moveBefore` 的浏览器退回旧行为(`appendChild` → 整页重载),
540
563
  状态里以 `degraded` 明示;详见下方「为什么切标签不再重载」。
541
564
  - **远程访问**:`serve: dsh` 下浏览器只需能到达 DSH 本身(单一端口,认证与 `/api` 同级);
542
- `serve: loopback` 默认仅回环、`auth: none`,跨机访问请改用 `serve: dsh`
543
- (0.2.0 起不再支持 `auth: password`)。
565
+ `serve: loopback` 仅回环绑定(随机端口 + 路径令牌 + Host 白名单,见「回环端口的安全模型」),
566
+ 跨机访问请改用 `serve: dsh`(0.2.0 起不再支持 `auth: password`)。
567
+ - **回环模式的令牌会随实例轮换**:每次新启动端口与令牌都变;`adopt`(host 重启后接管存活实例)靠
568
+ `endpoint.json` + `path-token` 两个文件对上,所以**别手动删**这两个文件(删了 host 认不出旧实例,
569
+ 会当成陌生端口占用处理)。
package/lib/client.js CHANGED
@@ -1,5 +1,5 @@
1
1
  window.__ModuleLoader__.load({id:'dsh-code-server-app',factory:function(require){var module={exports:{}};var exports=module.exports;
2
- var H=Object.defineProperty;var ge=Object.getOwnPropertyDescriptor;var ye=Object.getOwnPropertyNames;var xe=Object.prototype.hasOwnProperty;var we=(e,r)=>{for(var n in r)H(e,n,{get:r[n],enumerable:!0})},Ee=(e,r,n,t)=>{if(r&&typeof r=="object"||typeof r=="function")for(let l of ye(r))!xe.call(e,l)&&l!==n&&H(e,l,{get:()=>r[l],enumerable:!(t=ge(r,l))||t.enumerable});return e};var ke=e=>Ee(H({},"__esModule",{value:!0}),e);var hr={};we(hr,{apply:()=>ur,inject:()=>pr,name:()=>vr});module.exports=ke(hr);var q="dsh-resource://file/",Se="sidebar://";function F(e){return typeof e=="string"&&e.startsWith(Se)}function D(e){if(typeof e!="string"||!e.startsWith(q))return null;for(var r=e.length,n=0;n<e.length;n+=1){var t=e.charAt(n);if(t==="?"||t==="#"){r=n;break}}var l=e.slice(q.length,r).split("/"),s=l.shift();try{if(s==="session"){var c=l.shift();return c===void 0||c===""||l.length===0?null:{scope:"session",sessionId:decodeURIComponent(c),path:K(l)}}if(s==="absolute"){var u=l[0]===""&&l.length>1,p=u?l.slice(1):l,o=K(p);return(o===""||o.charAt(0)==="/")&&!u?null:u?{scope:"absolute",path:"//"+o}:o===""?null:{scope:"absolute",path:$(o)?o:"/"+o}}}catch(f){return null}return null}function K(e){for(var r=[],n=0;n<e.length;n+=1)r.push(decodeURIComponent(e[n]));return r.join("/")}function $(e){return/^[A-Za-z]:($|\/)/.test(e)}function Ce(e){return typeof e!="string"||e===""?!1:e.charAt(0)==="/"?!0:$(e)}function ee(e){var r=D(e);if(r===null)return"";var n=r.path.replace(/[/\\]+$/,""),t=Math.max(n.lastIndexOf("/"),n.lastIndexOf("\\")),l=t===-1?n:n.slice(t+1);return l}function re(e,r){if(e===null||e==null)return null;var n=typeof e.path=="string"?e.path:"";if(n==="")return null;if(Ce(n))return n;if(typeof r!="string"||r==="")return null;var t=r.indexOf("\\")!==-1?"\\":"/",l=r.replace(/[\\/]+$/,"");return l+t+(t==="\\"?n.replace(/\//g,"\\"):n)}var T="*;!md;!markdown;!html;!htm;!png;!jpg;!jpeg;!gif;!webp;!bmp;!ico;!svg;!pdf",Ne=/[;,\s]+/;function Oe(e){return String(e).replace(/!\s+/g,"!").split(Ne)}function Le(e){var r=typeof e=="string"?e.trim():"";if(r==="")return"";var n=r.charAt(0)==="!",t=n?r.slice(1).trim():r;return t==="*"?n?"!*":"*":(t.slice(0,2)==="*."?t=t.slice(2):t.charAt(0)==="."&&(t=t.slice(1)),t=t.toLowerCase(),t===""?"":n?"!"+t:t)}function C(e){if(typeof e!="string")return"";for(var r=Oe(e),n=[],t=0;t<r.length;t+=1){var l=Le(r[t]);l===""||l==="!*"||n.indexOf(l)===-1&&n.push(l)}return n.join(";")}function P(e){for(var r=C(e),n=r===""?[]:r.split(";"),t=!1,l=[],s=[],c=0;c<n.length;c+=1){var u=n[c];if(u==="*"){t=!0;continue}u.charAt(0)==="!"?s.push(u.slice(1)):l.push(u)}return{all:t,allow:l,deny:s}}function Ae(e){if(typeof e!="string"||e==="")return"";var r=e.replace(/[/\\]+$/,""),n=Math.max(r.lastIndexOf("/"),r.lastIndexOf("\\"));n!==-1&&(r=r.slice(n+1));var t=r.lastIndexOf(".");return t<=0?"":r.slice(t+1).toLowerCase()}function Te(e,r){if(r==null)return!1;var n=Ae(e);return n!==""&&r.deny.indexOf(n)!==-1?!1:n!==""&&r.allow.indexOf(n)!==-1?!0:r.all===!0}function ne(e,r){return e===null||e==null?!1:Te(e.path,r)}function te(e){var r=P(e),n=[];return r.all&&n.push("\u5176\u4F59\u7C7B\u578B\u5168\u90E8\u8BA4\u9886"),r.allow.length>0&&n.push("\u6307\u5B9A\u8BA4\u9886 "+r.allow.length+" \u9879"),r.deny.length>0&&n.push("\u6392\u9664 "+r.deny.length+" \u9879"),n.length===0?"\u4E0D\u8BA4\u9886\u4EFB\u4F55\u6587\u4EF6(\u53EA\u6709\u9875\u9762\u6807\u7B7E)":n.join(" \xB7 ")}var Re="[data-sidebar-right-panel]",Me='[data-sidebar-right-mode="fullscreen"]';function ae(e){if(e==null||typeof e.closest!="function")return"no-root";var r,n;try{if(r=e.closest(Re),r==null||typeof r.querySelector!="function")return"no-panel";n=r.querySelector(Me)}catch(t){return"lookup-failed"}if(n==null||typeof n.click!="function")return"no-control";try{n.click()}catch(t){return"click-failed"}return"clicked"}var R=typeof Element!="undefined"&&typeof Element.prototype.moveBefore=="function",Ie=-2e4,a={park:null,frame:null,currentSrc:null,sameOrigin:null,owner:null,lastRect:null,parkStrategy:"offscreen",degraded:!1,preloaded:!1,nudgeCount:0,nudgeEnabled:!0,lastNudgeAt:0,listeners:new Set};function B(){return{ready:a.frame!==null,docked:a.owner!==null,owner:a.owner,src:a.currentSrc,sameOrigin:a.sameOrigin,parkStrategy:a.parkStrategy,degraded:a.degraded,preloaded:a.preloaded,supportsMoveBefore:R,nudgeCount:a.nudgeCount,lastNudgeAt:a.lastNudgeAt}}function M(){var e=B();a.listeners.forEach(function(r){try{r(e)}catch(n){}})}function le(e){return a.listeners.add(e),function(){a.listeners.delete(e)}}function ie(){return B()}function je(e){e!=="offscreen"&&e!=="behind"||a.parkStrategy!==e&&(a.parkStrategy=e,a.frame!==null&&a.owner===null&&G(),M())}function ze(){return a.parkStrategy}function De(){var e=a.lastRect;e!==null&&(a.park.style.width=Math.max(1,Math.round(e.width))+"px",a.park.style.height=Math.max(1,Math.round(e.height))+"px")}function G(){if(a.park!==null){if(De(),a.parkStrategy==="behind"&&a.lastRect!==null){a.park.style.left=Math.round(a.lastRect.left)+"px",a.park.style.top=Math.round(a.lastRect.top)+"px",a.park.style.visibility="visible",a.park.style.zIndex="0";return}a.park.style.left=Ie+"px",a.park.style.top="0px",a.park.style.visibility="hidden",a.park.style.zIndex="0"}}function se(){a.frame!==null&&(a.frame.setAttribute("inert",""),a.frame.setAttribute("aria-hidden","true"))}function Pe(){a.frame!==null&&(a.frame.removeAttribute("inert"),a.frame.removeAttribute("aria-hidden"))}function Ue(){var e=document.createElement("div");e.className="dshcs-park",e.setAttribute("data-dshcs-park",""),e.setAttribute("aria-hidden","true");var r=document.createElement("iframe");return r.className="dshcs-frame",r.setAttribute("title","code-server"),r.setAttribute("allow","clipboard-read; clipboard-write"),r.setAttribute("data-dshcs-resident",""),r.src="about:blank",e.appendChild(r),document.body.appendChild(e),a.park=e,a.frame=r,a.currentSrc="about:blank",a.owner=null,G(),se(),r}function _e(e){var r=a.frame;if(r!==null){var n=a.sameOrigin!==e;!n&&r.hasAttribute("data-dshcs-attrs")||(e===!0?r.removeAttribute("sandbox"):e===!1&&r.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-modals allow-popups allow-pointer-lock allow-clipboard-read allow-clipboard-write"),r.setAttribute("data-dshcs-attrs",e===!0?"same-origin":"cross-origin"),a.sameOrigin=e)}}function Q(e){var r=e||{};return a.frame===null&&Ue(),typeof r.sameOrigin=="boolean"&&_e(r.sameOrigin),typeof r.src=="string"&&r.src!==""&&r.src!==a.currentSrc&&He(r.src),a.frame}function He(e){a.frame===null||typeof e!="string"||e===""||e===a.currentSrc||(a.currentSrc=e,a.frame.src=e,M())}function ce(){var e=a.frame;if(a.nudgeEnabled!==!0||e===null||e.isConnected!==!0)return!1;var r=e.style.display;return e.style.display="none",e.offsetHeight,e.style.display=r,a.nudgeCount+=1,a.lastNudgeAt=Date.now(),!0}function Fe(e){return a.nudgeEnabled=e!==!1,a.nudgeEnabled}function oe(e){if(a.frame!==null){if(R)try{e.moveBefore(a.frame,null);return}catch(r){a.degraded=!0,a.lastMoveError=(r&&r.name?r.name+": ":"")+(r&&r.message?r.message:String(r))}else a.degraded=!0;e.appendChild(a.frame)}}function W(e,r){if(!(a.frame===null||e==null)){var n=a.owner===null,t=e.getBoundingClientRect();t.width>=1&&t.height>=1&&(a.lastRect={left:t.left,top:t.top,width:t.width,height:t.height}),a.owner=r===void 0?null:r,Pe(),a.park.setAttribute("aria-hidden","true"),oe(e),n&&ce(),M()}}function I(e){a.frame!==null&&(e!==void 0&&a.owner!==e||(a.owner=null,G(),se(),a.park.setAttribute("aria-hidden","true"),oe(a.park),M()))}function Y(e){var r=e||{};return Q(r),a.owner===null&&(typeof r.src=="string"&&r.src!==""&&(a.preloaded=!0),I()),a.frame}function Be(){a.park!==null&&a.park.parentNode!==null&&a.park.parentNode.removeChild(a.park),a.park=null,a.frame=null,a.currentSrc=null,a.sameOrigin=null,a.owner=null,a.lastRect=null,a.degraded=!1,a.preloaded=!1,a.nudgeCount=0,a.lastNudgeAt=0,M()}typeof window!="undefined"&&(window.__dshcsSurface={snapshot:B,setParkStrategy:je,getParkStrategy:ze,preload:Y,dock:W,park:I,nudge:ce,setNudgeEnabled:Fe,destroy:Be});var i=require("react"),X=new Set,j={status:null,busy:!1,sidebarUi:"unknown",sidebarRegisterFailed:!1};function S(e){j=Object.assign({},j,e),X.forEach(function(r){r()})}function Ge(e){return X.add(e),function(){X.delete(e)}}function Qe(){return j}function V(){return i.useSyncExternalStore(Ge,Qe)}var We="/api",Ye="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMTQ3IDE0NyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8c3R5bGU+QG1lZGlhIChwcmVmZXJzLWNvbG9yLXNjaGVtZTogZGFyaykgeyogeyBmaWxsOiB3aGl0ZTsgfX08L3N0eWxlPgogIDxwYXRoIGQ9Im00Mi40MjE0LDM5LjY1NWMtMjQuNDA1NywwIC00Mi4xNTU0LDEzLjE3MjEgLTQyLjE1NTQsMzMuODQ1YzAsMjAuNTgxNCAxOC40ODkyLDMzLjg0NSA0Mi4xNTU0LDMzLjg0NWMyMy42NjYyLDAgMzguMTgwMywtMTEuNjE3MSAzOC43MzQ5LC0yOC43MjI1bC0yMS4wNzc3LC0wLjQ1NzRjLTAuOTI0NCw5LjMzMDMgLTkuMTA1OSwxNS4xODQ1IC0xNy42NTcyLDE1LjE4NDVjLTExLjc0MDYsMCAtMjAuNDMwNiwtNy41OTIyIC0yMC40MzA2LC0xOS44NDk2YzAsLTEyLjI1NzQgOC42OSwtMTkuOTg2OCAyMC40MzA2LC0yMC4yMTU1YzguNTUxMywtMC4xODMgMTYuOTE3Nyw1Ljk0NTcgMTcuNDcyMywxNS4yNzZsMjEuMDc3NywtMC42NDAzYy0wLjQ2MjIsLTE2LjgzMTEgLTE0LjE0NDIsLTI4LjI2NTIgLTM4LjU1LC0yOC4yNjUyem00OC44NDQ2LDJsNTUuNDY4LDBsMCw2NC4wMzExbC01NS40NjgsMGwwLC02NC4wMzExeiIgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiLz4KPC9zdmc+Cg==";async function k(e,r){var n={method:r===void 0?"GET":"POST",headers:{}};r!==void 0&&(n.headers["content-type"]="application/json",n.body=JSON.stringify(r));var t=await fetch(We+e,n),l=await t.text(),s=null;try{s=l===""?null:JSON.parse(l)}catch(c){s=null}return t.ok?s!==null?s:{ok:!1,error:"invalid JSON response"}:{ok:!1,error:"HTTP "+t.status+(s!==null&&s.error!==void 0?": "+s.error:"")}}function me(e,r){try{var n=e(function(h){return h}),t=r===null?null:typeof r=="function"?r(function(h){return h}):null,l=n!=null?n.current:void 0;if(l!==void 0&&n!=null&&n.byId!=null){var s=n.byId[l];if(s!=null&&typeof s.cwd=="string"&&s.cwd!=="")return s.cwd}var c=t!=null&&Array.isArray(t.items)?t.items:[];if(l!==void 0)for(var u=0;u<c.length;u++){var p=c[u];if(p.sessionIds!=null&&p.sessionIds.indexOf(l)!==-1&&typeof p.path=="string"&&p.path!=="")return p.path}var o=t!=null?t.recentWorkspaceId:void 0;if(o!==void 0){for(var f=0;f<c.length;f++)if(c[f].workspaceId===o&&typeof c[f].path=="string"&&c[f].path!=="")return c[f].path}if(c.length>0&&typeof c[0].path=="string"&&c[0].path!=="")return c[0].path}catch(h){}}function be(e,r){if(e==null||typeof e.url!="string"||e.url==="")return null;var n=e.pid!=null?String(e.pid):e.startedAt!=null?String(e.startedAt):"0",t=e.url+"?s="+encodeURIComponent(n),l=typeof r=="string"&&r!==""?r:e!=null&&e.cwd!=null?e.cwd:null;if(l==null||l==="")return t;var s=l.replace(/\\/g,"/"),c=s;return(/^[A-Za-z]:\//.test(s)||s.charCodeAt(0)!==47)&&(c="/"+s),t+"&folder="+encodeURIComponent(c)}function Ze(e){var r=e.status,n=e.pageUrl,t=e.reloadTick,l=e.owner,s=i.useRef(null),c=r!=null&&r.ok===!0&&r.running===!0,u=r!=null&&r.status==="starting",p=r!=null&&r.ok===!1,o=r!=null&&r.serve==="dsh";if(i.useLayoutEffect(function(){if(!c||n===null){I(l);return}Q({src:n,sameOrigin:o}),s.current!==null&&W(s.current,l)},[c,n,o,t,l]),i.useLayoutEffect(function(){return function(){I(l)}},[l]),c&&n!==null)return i.createElement("div",{ref:s,className:"dshcs-slot","data-code-server-slot":l!=null?l:"code-server"});if(u)return i.createElement("div",{className:"dshcs-loading"},"\u6B63\u5728\u542F\u52A8 code-server\u2026");var f=p&&r!=null&&r.error?r.error:"code-server \u672A\u8FD0\u884C",h=o?"\u5F53\u524D\u4EE5 DSH \u540C\u6E90\u8DEF\u5F84 "+(r!=null&&r.url||"/code-server/")+" \u63D0\u4F9B(\u65E0\u72EC\u7ACB\u7AEF\u53E3)\u3002":"\u5F53\u524D\u4EE5\u72EC\u7ACB\u56DE\u73AF\u7AEF\u53E3\u63D0\u4F9B(\u7AEF\u53E3 "+(r!=null&&r.port!=null?r.port:"8090")+" \u88AB\u5360\u7528\u65F6\u8BF7\u91CA\u653E\u6216\u4FEE\u6539 port \u914D\u7F6E)\u3002",w=R?"\u5E38\u9A7B\u9762:\u53EF\u7528(\u5207\u6807\u7B7E/\u6536\u8D77\u4FA7\u680F\u4E0D\u91CD\u8F7D)\u3002":"\u5E38\u9A7B\u9762:\u5F53\u524D\u6D4F\u89C8\u5668\u4E0D\u652F\u6301 Element.moveBefore \u2014\u2014 \u5207\u6807\u7B7E\u4F1A\u6574\u9875\u91CD\u8F7D(\u5347\u7EA7\u6D4F\u89C8\u5668\u540E\u81EA\u52A8\u53EF\u7528)\u3002";return i.createElement("div",{className:"dshcs-empty"},i.createElement("div",{className:"dshcs-emptybox"},i.createElement("div",null,p?"code-server \u542F\u52A8\u5931\u8D25":"code-server \u672A\u8FD0\u884C"),i.createElement("pre",{className:"dshcs-error"},f),i.createElement("p",{className:"dshcs-hint"},"VS Code \u6811\u968F\u63D2\u4EF6\u5305\u5185\u7F6E\u3001\u5C31\u5730\u8FD0\u884C(\u65E0\u9700\u5168\u5C40\u5B89\u88C5\u3001\u65E0\u9700\u8054\u7F51\u5B89\u88C5\u3001\u65E0\u300C\u5B89\u88C5\u73AF\u5883\u300D\u6B65\u9AA4);\u5185\u90E8\u4F9D\u8D56\u4E0E\u9884\u7F16\u8BD1\u539F\u751F\u6A21\u5757\u7531\u5305\u7BA1\u7406\u5668\u5728\u5B89\u88C5\u63D2\u4EF6\u65F6\u4E00\u5E76\u88C5\u597D(\u65E0\u9700 C++ \u5DE5\u5177\u94FE)\u3002\u82E5\u6B64\u5904\u957F\u671F\u672A\u8FD0\u884C,\u8BF7\u67E5\u770B DSH host \u65E5\u5FD7\u91CC\u7684 [code-server] \u8F93\u51FA(\u6216\u5728\u8BBE\u7F6E\u5361\u7247\u91CC\u5207\u6362\u300C\u540E\u53F0\u5E38\u9A7B\u300D\u89E6\u53D1\u4E00\u6B21\u542F\u52A8)\u3002"+h+w)))}function Je(e){var r=V(),n=r.status,t=n!=null&&n.ok===!0&&n.running===!0,l=n!=null&&n.keepResident===!0,s=me(e&&e.useSessions,e&&e.useWorkspaces),c=t?be(n,s):null;return i.useEffect(function(){!l||!t||c===null||Y({src:c,sameOrigin:n!=null&&n.serve==="dsh"})},[l,t,c,n!=null?n.serve:null]),null}var Xe=".dshcs-frame{display:block;position:absolute;inset:0;z-index:1;width:100%;height:100%;border:0;background:var(--dsw-alias-bg-base,#fff)}.dshcs-tabroot{position:relative;display:flex;flex-direction:column;width:100%;height:100%;min-width:0;min-height:0;background:var(--dsw-alias-bg-base,#fff)}.dshcs-slot{position:relative;display:block;width:100%;height:100%;min-width:0;min-height:0;overflow:hidden;background:var(--dsw-alias-bg-base,#fff)}.dshcs-park{position:fixed;left:-20000px;top:0;width:320px;height:200px;overflow:hidden;pointer-events:none;z-index:0;visibility:hidden;contain:strict}.dshcs-loading{position:absolute;inset:0;z-index:2;display:grid;place-items:center;background:var(--dsw-alias-bg-base,#fff);color:var(--dsw-alias-label-tertiary,#7d8798);font-size:13px}.dshcs-empty{position:relative;z-index:2;flex:1;display:flex;align-items:center;justify-content:center;padding:44px 24px 24px}.dshcs-emptybox{max-width:560px;text-align:left;background:var(--dsw-alias-bg-layer-2,#f7f8fb);border:1px solid var(--dsw-alias-border-l2,#dfe3eb);border-radius:12px;padding:16px 18px}.dshcs-error{color:var(--dsw-alias-label-error,#d92d20);white-space:pre-wrap;word-break:break-all;font-family:ui-monospace,Consolas,monospace;font-size:12px;margin:8px 0 0}.dshcs-hint{color:var(--dsw-alias-label-tertiary,#7d8798);font-size:12px;margin:6px 0 0}.dshcs-legacy{color:var(--dsw-alias-label-warning,#b54708);font-size:12px;font-weight:600;margin:0 0 6px}",de="dsh-code-server/styles";typeof document!="undefined"&&document.querySelector("style[data-dshcs="+JSON.stringify(de)+"]")===null&&(U=document.createElement("style"),U.setAttribute("data-dshcs",de),U.textContent=Xe,document.head.appendChild(U));var U,ue="code-server",fe="dsh-code-server-app",Ve=2500,qe=1e4,Ke=1500;function $e(e){var r=e!=null&&typeof e.size=="number"?e.size:16;return i.createElement("img",{src:Ye,alt:"","aria-hidden":!0,draggable:!1,className:e!=null?e.className:void 0,style:{width:r,height:r,display:"block",objectFit:"contain",WebkitUserDrag:"none",userSelect:"none"}})}var er=P("");function rr(){var e=j.status;return e==null||typeof e.claimExtensions!="string"?er:P(e.claimExtensions)}function nr(e,r){try{var n=e(function(l){return l});if(n!=null&&r!=null&&n.byId!=null){var t=n.byId[r];if(t!=null&&typeof t.cwd=="string"&&t.cwd!=="")return t.cwd}}catch(l){}}function tr(e){var r=e.useTabInfo(),n=r.tab,t=V(),l=t.status,s=n.navigation,c=s!=null&&typeof s.revision=="number"?s.revision:0,u=s!=null&&typeof s.address=="string"?s.address:"",p=F(u)?null:D(u),o=nr(e.useSessions,p!=null?p.sessionId:void 0),f=o!==void 0?o:me(e.useSessions,null),h=re(p,f),w=s!=null&&s.params!=null&&typeof s.params.line=="number"?s.params.line:null,d=i.useRef(void 0),[g,v]=i.useState(0),x=i.useRef(null),L=i.useRef(!1),z=r.tab!=null&&r.tab.visible===!0,N=r.sidebar!=null&&r.sidebar.fullscreen===!0,_=l!=null&&l.fullscreenOnOpen!==!1;return i.useLayoutEffect(function(){if(!z){L.current=!1;return}if(!(!_||N||L.current)){L.current=!0;var E=ae(x.current);E!=="clicked"&&console.warn("[code-server] \u672A\u80FD\u81EA\u52A8\u5207\u5230\u5168\u5C4F\u53F3\u4FA7\u680F("+E+");\u672C\u6B21\u6253\u5F00\u4FDD\u6301\u539F\u6A21\u5F0F")}},[z,N,_]),i.useEffect(function(){if(!(typeof f!="string"||f==="")&&d.current!==f){var E=d.current!==void 0;d.current=f;var b=!1;return k("/code-server/start",{cwd:f}).then(function(O){b||(S({status:O}),E&&O!=null&&O.ok===!0&&O.running===!0&&v(function(A){return A+1}))}).catch(function(){}),function(){b=!0}}},[f]),i.useEffect(function(){k("/code-server/status").then(function(b){b!=null&&S({status:b})}).catch(function(){});var E=window.setInterval(function(){k("/code-server/status").then(function(b){S({status:b})}).catch(function(){})},3e3);return function(){window.clearInterval(E)}},[]),i.useEffect(function(){h!==null&&k("/code-server/open-file",w===null?{file:h}:{file:h,line:w}).catch(function(){})},[c,h,w]),i.createElement("div",{className:"dshcs-tabroot","data-code-server-tab":"body",ref:x},i.createElement(Ze,{status:l,pageUrl:be(l,f),reloadTick:g,owner:"tab:"+n.id}))}function Z(e,r){if(e==null)return null;var n=e[r];if(n!=null)return n;if(typeof e.get=="function"){var t=e.get(r);if(t!=null)return t}return null}function ar(e){var r=Z(e,"sidebarRightTabs"),n=Z(e,"sidebarRight");if(r==null||n==null)return console.error("[code-server] \u53F3\u4FA7\u680F\u670D\u52A1\u4E0A\u4E0B\u6587\u4E0D\u53EF\u89C1(sidebarRightTabs="+(r!=null)+", sidebarRight="+(n!=null)+"):\u8DF3\u8FC7\u4FA7\u680F\u6CE8\u518C"),!1;var t=Z(e,"slots");if(e.effect(function(){return r.register({id:fe,kind:ue,priority:"extension",patterns:["dsh-resource://file/**"],canOpen:function(l){return ne(D(l),rr())},title:function(l){if(F(l)||l===void 0||l===null||l==="")return"Code Server";var s=ee(l);return s===""?"Code Server":s},guide:[{order:20,title:function(){return"Code Server"},description:function(){return"\u5728\u53F3\u4FA7\u680F\u6807\u7B7E\u91CC\u8FD0\u884C VS Code \u7F51\u9875\u7248,\u8DDF\u968F\u5F53\u524D\u4F1A\u8BDD\u5DE5\u4F5C\u533A;\u4EA7\u7269/\u4EA4\u4ED8\u6587\u4EF6\u70B9\u51FB\u540E\u5728\u6B64\u6253\u5F00\u3002"},icon:$e}]})},"code-server: sidebar tab type"),t!=null)e.effect(function(){return t.inject("sidebar.right.pane.tab",function(){return t.register({name:"sidebar.right.pane.tab",key:fe},tr)})},"code-server: sidebar tab body");else return console.error("[code-server] slots \u670D\u52A1\u4E0D\u53EF\u89C1:\u4FA7\u680F\u6807\u7B7E body \u672A\u6CE8\u518C"),!1;return e.effect(function(){return function(){S({sidebarUi:"unknown"})}},"code-server: sidebar mode reset"),console.log("[code-server] right-sidebar tab registered (kind="+ue+")"),!0}var lr=".dshcs-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.dshcs-card:hover{border-color:var(--dsw-alias-label-dimmed,var(--dsw-alias-border-l2))}.dshcs-cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed,var(--dsw-alias-border-l2))}.dshcs-cardHeader{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.dshcs-cardHeader:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.dshcs-cardHeadText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.dshcs-cardName{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.dshcs-cardDescription{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));font-size:13px;line-height:1.5}.dshcs-cardChevron{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));flex:none;transition:transform .16s;display:inline-flex}.dshcs-cardChevronOpen{transform:rotate(180deg)}.dshcs-cardBody{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.dshcs-cardReadOnly{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));margin:12px 0 0;font-size:12px;line-height:1.5}.dshcs-pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.dshcs-cardFooter{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.dshcs-cardFailed{min-width:0;color:var(--dsw-alias-label-error,var(--dsw-alias-state-error-primary));flex:1;margin:0;font-size:12px;line-height:1.5}.dshcs-cbtn{appearance:none;font:inherit;cursor:pointer;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5;border:1px solid transparent;transition:color .12s ease,background .12s ease}.dshcs-cbtn:disabled{opacity:.4;cursor:default}.dshcs-cbtn:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.dshcs-cbtnOutline{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.dshcs-cbtnOutline:hover:not(:disabled){color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover,transparent)}.dshcs-cbtnPrimary{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3);border-color:var(--dsw-alias-label-primary);font-weight:600}.dshcs-cbtnPrimary:hover:not(:disabled){filter:brightness(.95)}.dshcs-cbtnPrimary:active:not(:disabled){filter:brightness(.9)}.dshcs-field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.dshcs-field+.dshcs-field{border-top:1px solid var(--dsw-alias-border-l2)}.dshcs-fieldHead{align-items:center;gap:8px;display:flex}.dshcs-fieldLabel{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.dshcs-card .dshcs-hint{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));margin:0;font-size:12px;line-height:1.5}.dshcs-check{display:flex;align-items:center;gap:8px;cursor:pointer}.dshcs-check input{accent-color:var(--dsw-alias-brand-primary);width:15px;height:15px;margin:0;flex:none}.dshcs-check input:disabled{cursor:default}.dshcs-text{display:flex}.dshcs-text input{font:12px/1.5 ui-monospace,Consolas,monospace;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3,transparent);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:5px 8px;width:100%;min-width:0}.dshcs-text input:disabled{opacity:.5;cursor:default}.dshcs-text input:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-1px}.dshcs-badges{align-items:center;gap:8px;display:inline-flex}.dshcs-badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.dshcs-reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.dshcs-reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.dshcs-reset:disabled{cursor:default}",ir="dsh-code-server/Card.module.css";function sr(e,r){if(typeof document!="undefined"&&document.querySelector("style[data-plugin-css="+JSON.stringify(e)+"]")===null){var n=document.createElement("style");n.dataset.plugin="dsh-code-server",n.dataset.pluginCss=e,n.textContent=r,document.head.appendChild(n)}}sr(ir,lr);function cr(e){return i.createElement("svg",{width:14,height:14,viewBox:"0 0 14 14",fill:"none","aria-hidden":!0,className:e.className},i.createElement("path",{d:"M3.5 5.25L7 8.75L10.5 5.25",stroke:"currentColor",strokeWidth:1.5,strokeLinecap:"round",strokeLinejoin:"round"}))}function pe(e){return i.createElement("button",{type:"button",className:"dshcs-cbtn "+(e.variant==="primary"?"dshcs-cbtnPrimary":"dshcs-cbtnOutline"),disabled:e.disabled===!0,onClick:e.onClick},e.children)}function ve(e){return i.createElement("label",{className:"dshcs-check"},i.createElement("input",{type:"checkbox",checked:e.checked===!0,disabled:e.disabled===!0,onChange:function(r){e.onChange(r.target.checked)}}),i.createElement("span",{className:"dshcs-hint"},e.children))}function or(e){return i.createElement("label",{className:"dshcs-text"},i.createElement("input",{type:"text",value:e.value,disabled:e.disabled===!0,spellCheck:!1,autoComplete:"off",placeholder:e.placeholder,onChange:function(r){e.onChange(r.target.value)}}))}function J(e){var r=[];return e.overridden===!0&&(r.push(i.createElement("span",{className:"dshcs-badge"},e.overriddenLabel)),r.push(i.createElement("button",{type:"button",className:"dshcs-reset",disabled:e.disabled===!0,onClick:e.onReset},e.resetLabel))),r.length===0?null:i.createElement("span",{className:"dshcs-badges"},...r)}function he(e){var[r,n]=i.useState(e.defaultOpen===!0),t=e.state;if(t==null||t.available!==!0)return null;var l=e.title,s=e.noticeOnly===!0,c=t.dirty!==!0||t.invalid===!0||t.saving===!0;return i.createElement("li",{className:"dshcs-card"+(r?" dshcs-cardOpen":"")},i.createElement("button",{type:"button",className:"dshcs-cardHeader","aria-expanded":r,onClick:function(){n(!r)}},i.createElement("span",{className:"dshcs-cardHeadText"},i.createElement("span",{className:"dshcs-cardName"},l),i.createElement("span",{className:"dshcs-cardDescription"},e.description)),t.dirty===!0?i.createElement("span",{className:"dshcs-pending"},e.unsavedLabel):null,i.createElement(cr,{className:"dshcs-cardChevron"+(r?" dshcs-cardChevronOpen":"")})),r===!0?i.createElement("div",{className:"dshcs-cardBody"},s===!0?null:t.writable!==!0?i.createElement("p",{className:"dshcs-cardReadOnly",role:"status"},e.readOnlyLabel):null,e.children,s===!0?null:i.createElement("div",{className:"dshcs-cardFooter"},t.failed===!0?i.createElement("p",{className:"dshcs-cardFailed",role:"status"},e.saveFailedLabel):null,i.createElement(pe,{disabled:t.dirty!==!0||t.saving===!0,onClick:e.onDiscard},e.discardLabel),i.createElement(pe,{variant:"primary",disabled:c,onClick:e.onSave},t.saving===!0?e.savingLabel:e.saveLabel))):null)}function dr(e){try{let b=function(){k("/code-server/status").then(function(m){m!=null&&typeof m=="object"&&S({status:m})}).catch(function(){})};var r=e.scope,[n,t]=i.useState(function(){return r!==void 0?r.getSnapshot():null}),[l,s]=i.useState(null),[c,u]=i.useState(!1),[p,o]=i.useState(!1),f=V(),[h,w]=i.useState(ie());if(i.useEffect(function(){return le(function(m){w(m)})},[]),i.useEffect(function(){if(r===void 0||typeof r.subscribe!="function")return;function m(){try{t(r.getSnapshot())}catch(mr){}}var y=r.subscribe(m);return function(){typeof y=="function"&&y()}},[r]),r===void 0)return console.error("[code-server] card scope missing"),null;if(n==null||n.status!=="ready")return n!=null&&n.status==="unavailable"&&console.warn("[code-server] settings scope unavailable:",n.status),null;var d=n.value!==void 0&&n.value!==null?n.value:{},g=n.user,v={keepResident:d.keepResident!==!1,claimExtensions:typeof d.claimExtensions=="string"?d.claimExtensions:T,fullscreenOnOpen:d.fullscreenOnOpen!==!1};if(f!=null&&f.sidebarUi==="legacy")return i.createElement(he,{title:"Code Server",description:"\u5F53\u524D DSH \u7248\u672C\u4E0D\u53D7\u652F\u6301(\u7F3A\u5C11\u53F3\u4FA7\u680F\u670D\u52A1)",defaultOpen:!0,noticeOnly:!0,state:{available:!0,writable:!1,dirty:!1,invalid:!1,saving:!1,failed:!1}},i.createElement("div",{className:"dshcs-field"},i.createElement("div",{className:"dshcs-legacy"},"\u672C\u63D2\u4EF6\u81EA 0.2.3 \u8D77\u4E0D\u518D\u517C\u5BB9\u65E7\u7248 DSH\u3002"),i.createElement("div",{className:"dshcs-hint",style:{marginTop:0}},`\u672A\u68C0\u6D4B\u5230\u53F3\u4FA7\u680F\u63D2\u4EF6\u670D\u52A1 sidebarRightTabs / sidebarRight,\u56E0\u6B64\u63D2\u4EF6\u4E0D\u63D0\u4F9B\u4EFB\u4F55\u5165\u53E3(\u65E7\u7248\u7684\u60AC\u6D6E\u7403\u4E0E\u6D6E\u52A8\u7A97\u53E3\u5DF2\u79FB\u9664),\u4E5F\u4E0D\u4F1A\u540E\u53F0\u542F\u52A8 IDE\u3002
2
+ var H=Object.defineProperty;var ge=Object.getOwnPropertyDescriptor;var ye=Object.getOwnPropertyNames;var xe=Object.prototype.hasOwnProperty;var we=(e,r)=>{for(var n in r)H(e,n,{get:r[n],enumerable:!0})},Ee=(e,r,n,t)=>{if(r&&typeof r=="object"||typeof r=="function")for(let l of ye(r))!xe.call(e,l)&&l!==n&&H(e,l,{get:()=>r[l],enumerable:!(t=ge(r,l))||t.enumerable});return e};var ke=e=>Ee(H({},"__esModule",{value:!0}),e);var hr={};we(hr,{apply:()=>ur,inject:()=>pr,name:()=>vr});module.exports=ke(hr);var q="dsh-resource://file/",Se="sidebar://";function F(e){return typeof e=="string"&&e.startsWith(Se)}function D(e){if(typeof e!="string"||!e.startsWith(q))return null;for(var r=e.length,n=0;n<e.length;n+=1){var t=e.charAt(n);if(t==="?"||t==="#"){r=n;break}}var l=e.slice(q.length,r).split("/"),s=l.shift();try{if(s==="session"){var c=l.shift();return c===void 0||c===""||l.length===0?null:{scope:"session",sessionId:decodeURIComponent(c),path:K(l)}}if(s==="absolute"){var u=l[0]===""&&l.length>1,p=u?l.slice(1):l,o=K(p);return(o===""||o.charAt(0)==="/")&&!u?null:u?{scope:"absolute",path:"//"+o}:o===""?null:{scope:"absolute",path:$(o)?o:"/"+o}}}catch(f){return null}return null}function K(e){for(var r=[],n=0;n<e.length;n+=1)r.push(decodeURIComponent(e[n]));return r.join("/")}function $(e){return/^[A-Za-z]:($|\/)/.test(e)}function Ce(e){return typeof e!="string"||e===""?!1:e.charAt(0)==="/"?!0:$(e)}function ee(e){var r=D(e);if(r===null)return"";var n=r.path.replace(/[/\\]+$/,""),t=Math.max(n.lastIndexOf("/"),n.lastIndexOf("\\")),l=t===-1?n:n.slice(t+1);return l}function re(e,r){if(e===null||e==null)return null;var n=typeof e.path=="string"?e.path:"";if(n==="")return null;if(Ce(n))return n;if(typeof r!="string"||r==="")return null;var t=r.indexOf("\\")!==-1?"\\":"/",l=r.replace(/[\\/]+$/,"");return l+t+(t==="\\"?n.replace(/\//g,"\\"):n)}var T="*;!md;!markdown;!html;!htm;!png;!jpg;!jpeg;!gif;!webp;!bmp;!ico;!svg;!pdf",Ne=/[;,\s]+/;function Oe(e){return String(e).replace(/!\s+/g,"!").split(Ne)}function Le(e){var r=typeof e=="string"?e.trim():"";if(r==="")return"";var n=r.charAt(0)==="!",t=n?r.slice(1).trim():r;return t==="*"?n?"!*":"*":(t.slice(0,2)==="*."?t=t.slice(2):t.charAt(0)==="."&&(t=t.slice(1)),t=t.toLowerCase(),t===""?"":n?"!"+t:t)}function C(e){if(typeof e!="string")return"";for(var r=Oe(e),n=[],t=0;t<r.length;t+=1){var l=Le(r[t]);l===""||l==="!*"||n.indexOf(l)===-1&&n.push(l)}return n.join(";")}function P(e){for(var r=C(e),n=r===""?[]:r.split(";"),t=!1,l=[],s=[],c=0;c<n.length;c+=1){var u=n[c];if(u==="*"){t=!0;continue}u.charAt(0)==="!"?s.push(u.slice(1)):l.push(u)}return{all:t,allow:l,deny:s}}function Ae(e){if(typeof e!="string"||e==="")return"";var r=e.replace(/[/\\]+$/,""),n=Math.max(r.lastIndexOf("/"),r.lastIndexOf("\\"));n!==-1&&(r=r.slice(n+1));var t=r.lastIndexOf(".");return t<=0?"":r.slice(t+1).toLowerCase()}function Te(e,r){if(r==null)return!1;var n=Ae(e);return n!==""&&r.deny.indexOf(n)!==-1?!1:n!==""&&r.allow.indexOf(n)!==-1?!0:r.all===!0}function ne(e,r){return e===null||e==null?!1:Te(e.path,r)}function te(e){var r=P(e),n=[];return r.all&&n.push("\u5176\u4F59\u7C7B\u578B\u5168\u90E8\u8BA4\u9886"),r.allow.length>0&&n.push("\u6307\u5B9A\u8BA4\u9886 "+r.allow.length+" \u9879"),r.deny.length>0&&n.push("\u6392\u9664 "+r.deny.length+" \u9879"),n.length===0?"\u4E0D\u8BA4\u9886\u4EFB\u4F55\u6587\u4EF6(\u53EA\u6709\u9875\u9762\u6807\u7B7E)":n.join(" \xB7 ")}var Re="[data-sidebar-right-panel]",Me='[data-sidebar-right-mode="fullscreen"]';function ae(e){if(e==null||typeof e.closest!="function")return"no-root";var r,n;try{if(r=e.closest(Re),r==null||typeof r.querySelector!="function")return"no-panel";n=r.querySelector(Me)}catch(t){return"lookup-failed"}if(n==null||typeof n.click!="function")return"no-control";try{n.click()}catch(t){return"click-failed"}return"clicked"}var R=typeof Element!="undefined"&&typeof Element.prototype.moveBefore=="function",Ie=-2e4,a={park:null,frame:null,currentSrc:null,sameOrigin:null,owner:null,lastRect:null,parkStrategy:"offscreen",degraded:!1,preloaded:!1,nudgeCount:0,nudgeEnabled:!0,lastNudgeAt:0,listeners:new Set};function B(){return{ready:a.frame!==null,docked:a.owner!==null,owner:a.owner,src:a.currentSrc,sameOrigin:a.sameOrigin,parkStrategy:a.parkStrategy,degraded:a.degraded,preloaded:a.preloaded,supportsMoveBefore:R,nudgeCount:a.nudgeCount,lastNudgeAt:a.lastNudgeAt}}function M(){var e=B();a.listeners.forEach(function(r){try{r(e)}catch(n){}})}function le(e){return a.listeners.add(e),function(){a.listeners.delete(e)}}function ie(){return B()}function je(e){e!=="offscreen"&&e!=="behind"||a.parkStrategy!==e&&(a.parkStrategy=e,a.frame!==null&&a.owner===null&&G(),M())}function ze(){return a.parkStrategy}function De(){var e=a.lastRect;e!==null&&(a.park.style.width=Math.max(1,Math.round(e.width))+"px",a.park.style.height=Math.max(1,Math.round(e.height))+"px")}function G(){if(a.park!==null){if(De(),a.parkStrategy==="behind"&&a.lastRect!==null){a.park.style.left=Math.round(a.lastRect.left)+"px",a.park.style.top=Math.round(a.lastRect.top)+"px",a.park.style.visibility="visible",a.park.style.zIndex="0";return}a.park.style.left=Ie+"px",a.park.style.top="0px",a.park.style.visibility="hidden",a.park.style.zIndex="0"}}function se(){a.frame!==null&&(a.frame.setAttribute("inert",""),a.frame.setAttribute("aria-hidden","true"))}function Pe(){a.frame!==null&&(a.frame.removeAttribute("inert"),a.frame.removeAttribute("aria-hidden"))}function Ue(){var e=document.createElement("div");e.className="dshcs-park",e.setAttribute("data-dshcs-park",""),e.setAttribute("aria-hidden","true");var r=document.createElement("iframe");return r.className="dshcs-frame",r.setAttribute("title","code-server"),r.setAttribute("allow","clipboard-read; clipboard-write"),r.setAttribute("data-dshcs-resident",""),r.src="about:blank",e.appendChild(r),document.body.appendChild(e),a.park=e,a.frame=r,a.currentSrc="about:blank",a.owner=null,G(),se(),r}function _e(e){var r=a.frame;if(r!==null){var n=a.sameOrigin!==e;!n&&r.hasAttribute("data-dshcs-attrs")||(e===!0?r.removeAttribute("sandbox"):e===!1&&r.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-modals allow-popups allow-pointer-lock allow-clipboard-read allow-clipboard-write"),r.setAttribute("data-dshcs-attrs",e===!0?"same-origin":"cross-origin"),a.sameOrigin=e)}}function Q(e){var r=e||{};return a.frame===null&&Ue(),typeof r.sameOrigin=="boolean"&&_e(r.sameOrigin),typeof r.src=="string"&&r.src!==""&&r.src!==a.currentSrc&&He(r.src),a.frame}function He(e){a.frame===null||typeof e!="string"||e===""||e===a.currentSrc||(a.currentSrc=e,a.frame.src=e,M())}function ce(){var e=a.frame;if(a.nudgeEnabled!==!0||e===null||e.isConnected!==!0)return!1;var r=e.style.display;return e.style.display="none",e.offsetHeight,e.style.display=r,a.nudgeCount+=1,a.lastNudgeAt=Date.now(),!0}function Fe(e){return a.nudgeEnabled=e!==!1,a.nudgeEnabled}function oe(e){if(a.frame!==null){if(R)try{e.moveBefore(a.frame,null);return}catch(r){a.degraded=!0,a.lastMoveError=(r&&r.name?r.name+": ":"")+(r&&r.message?r.message:String(r))}else a.degraded=!0;e.appendChild(a.frame)}}function W(e,r){if(!(a.frame===null||e==null)){var n=a.owner===null,t=e.getBoundingClientRect();t.width>=1&&t.height>=1&&(a.lastRect={left:t.left,top:t.top,width:t.width,height:t.height}),a.owner=r===void 0?null:r,Pe(),a.park.setAttribute("aria-hidden","true"),oe(e),n&&ce(),M()}}function I(e){a.frame!==null&&(e!==void 0&&a.owner!==e||(a.owner=null,G(),se(),a.park.setAttribute("aria-hidden","true"),oe(a.park),M()))}function Y(e){var r=e||{};return Q(r),a.owner===null&&(typeof r.src=="string"&&r.src!==""&&(a.preloaded=!0),I()),a.frame}function Be(){a.park!==null&&a.park.parentNode!==null&&a.park.parentNode.removeChild(a.park),a.park=null,a.frame=null,a.currentSrc=null,a.sameOrigin=null,a.owner=null,a.lastRect=null,a.degraded=!1,a.preloaded=!1,a.nudgeCount=0,a.lastNudgeAt=0,M()}typeof window!="undefined"&&(window.__dshcsSurface={snapshot:B,setParkStrategy:je,getParkStrategy:ze,preload:Y,dock:W,park:I,nudge:ce,setNudgeEnabled:Fe,destroy:Be});var i=require("react"),X=new Set,j={status:null,busy:!1,sidebarUi:"unknown",sidebarRegisterFailed:!1};function S(e){j=Object.assign({},j,e),X.forEach(function(r){r()})}function Ge(e){return X.add(e),function(){X.delete(e)}}function Qe(){return j}function V(){return i.useSyncExternalStore(Ge,Qe)}var We="/api",Ye="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMTQ3IDE0NyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8c3R5bGU+QG1lZGlhIChwcmVmZXJzLWNvbG9yLXNjaGVtZTogZGFyaykgeyogeyBmaWxsOiB3aGl0ZTsgfX08L3N0eWxlPgogIDxwYXRoIGQ9Im00Mi40MjE0LDM5LjY1NWMtMjQuNDA1NywwIC00Mi4xNTU0LDEzLjE3MjEgLTQyLjE1NTQsMzMuODQ1YzAsMjAuNTgxNCAxOC40ODkyLDMzLjg0NSA0Mi4xNTU0LDMzLjg0NWMyMy42NjYyLDAgMzguMTgwMywtMTEuNjE3MSAzOC43MzQ5LC0yOC43MjI1bC0yMS4wNzc3LC0wLjQ1NzRjLTAuOTI0NCw5LjMzMDMgLTkuMTA1OSwxNS4xODQ1IC0xNy42NTcyLDE1LjE4NDVjLTExLjc0MDYsMCAtMjAuNDMwNiwtNy41OTIyIC0yMC40MzA2LC0xOS44NDk2YzAsLTEyLjI1NzQgOC42OSwtMTkuOTg2OCAyMC40MzA2LC0yMC4yMTU1YzguNTUxMywtMC4xODMgMTYuOTE3Nyw1Ljk0NTcgMTcuNDcyMywxNS4yNzZsMjEuMDc3NywtMC42NDAzYy0wLjQ2MjIsLTE2LjgzMTEgLTE0LjE0NDIsLTI4LjI2NTIgLTM4LjU1LC0yOC4yNjUyem00OC44NDQ2LDJsNTUuNDY4LDBsMCw2NC4wMzExbC01NS40NjgsMGwwLC02NC4wMzExeiIgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiLz4KPC9zdmc+Cg==";async function k(e,r){var n={method:r===void 0?"GET":"POST",headers:{}};r!==void 0&&(n.headers["content-type"]="application/json",n.body=JSON.stringify(r));var t=await fetch(We+e,n),l=await t.text(),s=null;try{s=l===""?null:JSON.parse(l)}catch(c){s=null}return t.ok?s!==null?s:{ok:!1,error:"invalid JSON response"}:{ok:!1,error:"HTTP "+t.status+(s!==null&&s.error!==void 0?": "+s.error:"")}}function me(e,r){try{var n=e(function(h){return h}),t=r===null?null:typeof r=="function"?r(function(h){return h}):null,l=n!=null?n.current:void 0;if(l!==void 0&&n!=null&&n.byId!=null){var s=n.byId[l];if(s!=null&&typeof s.cwd=="string"&&s.cwd!=="")return s.cwd}var c=t!=null&&Array.isArray(t.items)?t.items:[];if(l!==void 0)for(var u=0;u<c.length;u++){var p=c[u];if(p.sessionIds!=null&&p.sessionIds.indexOf(l)!==-1&&typeof p.path=="string"&&p.path!=="")return p.path}var o=t!=null?t.recentWorkspaceId:void 0;if(o!==void 0){for(var f=0;f<c.length;f++)if(c[f].workspaceId===o&&typeof c[f].path=="string"&&c[f].path!=="")return c[f].path}if(c.length>0&&typeof c[0].path=="string"&&c[0].path!=="")return c[0].path}catch(h){}}function be(e,r){if(e==null||typeof e.url!="string"||e.url==="")return null;var n=e.pid!=null?String(e.pid):e.startedAt!=null?String(e.startedAt):"0",t=e.url+"?s="+encodeURIComponent(n),l=typeof r=="string"&&r!==""?r:e!=null&&e.cwd!=null?e.cwd:null;if(l==null||l==="")return t;var s=l.replace(/\\/g,"/"),c=s;return(/^[A-Za-z]:\//.test(s)||s.charCodeAt(0)!==47)&&(c="/"+s),t+"&folder="+encodeURIComponent(c)}function Ze(e){var r=e.status,n=e.pageUrl,t=e.reloadTick,l=e.owner,s=i.useRef(null),c=r!=null&&r.ok===!0&&r.running===!0,u=r!=null&&r.status==="starting",p=r!=null&&r.ok===!1,o=r!=null&&r.serve==="dsh";if(i.useLayoutEffect(function(){if(!c||n===null){I(l);return}Q({src:n,sameOrigin:o}),s.current!==null&&W(s.current,l)},[c,n,o,t,l]),i.useLayoutEffect(function(){return function(){I(l)}},[l]),c&&n!==null)return i.createElement("div",{ref:s,className:"dshcs-slot","data-code-server-slot":l!=null?l:"code-server"});if(u)return i.createElement("div",{className:"dshcs-loading"},"\u6B63\u5728\u542F\u52A8 code-server\u2026");var f=p&&r!=null&&r.error?r.error:"code-server \u672A\u8FD0\u884C",h=o?"\u5F53\u524D\u4EE5 DSH \u540C\u6E90\u8DEF\u5F84 "+(r!=null&&r.url||"/code-server/")+" \u63D0\u4F9B(\u65E0\u72EC\u7ACB\u7AEF\u53E3)\u3002":"\u5F53\u524D\u4EE5\u72EC\u7ACB\u56DE\u73AF\u7AEF\u53E3\u63D0\u4F9B(\u7AEF\u53E3 "+(r!=null&&r.port!=null?r.port:"\u968F\u673A")+" + \u8DEF\u5F84\u4EE4\u724C;\u8981\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00,\u590D\u5236\u8BBE\u7F6E\u5361\u91CC\u7684\u5B8C\u6574\u5730\u5740 \u2014\u2014 \u7F3A\u4E86\u4EE4\u724C\u90A3\u6BB5\u4F1A 404)\u3002",w=R?"\u5E38\u9A7B\u9762:\u53EF\u7528(\u5207\u6807\u7B7E/\u6536\u8D77\u4FA7\u680F\u4E0D\u91CD\u8F7D)\u3002":"\u5E38\u9A7B\u9762:\u5F53\u524D\u6D4F\u89C8\u5668\u4E0D\u652F\u6301 Element.moveBefore \u2014\u2014 \u5207\u6807\u7B7E\u4F1A\u6574\u9875\u91CD\u8F7D(\u5347\u7EA7\u6D4F\u89C8\u5668\u540E\u81EA\u52A8\u53EF\u7528)\u3002";return i.createElement("div",{className:"dshcs-empty"},i.createElement("div",{className:"dshcs-emptybox"},i.createElement("div",null,p?"code-server \u542F\u52A8\u5931\u8D25":"code-server \u672A\u8FD0\u884C"),i.createElement("pre",{className:"dshcs-error"},f),i.createElement("p",{className:"dshcs-hint"},"VS Code \u6811\u968F\u63D2\u4EF6\u5305\u5185\u7F6E\u3001\u5C31\u5730\u8FD0\u884C(\u65E0\u9700\u5168\u5C40\u5B89\u88C5\u3001\u65E0\u9700\u8054\u7F51\u5B89\u88C5\u3001\u65E0\u300C\u5B89\u88C5\u73AF\u5883\u300D\u6B65\u9AA4);\u5185\u90E8\u4F9D\u8D56\u4E0E\u9884\u7F16\u8BD1\u539F\u751F\u6A21\u5757\u7531\u5305\u7BA1\u7406\u5668\u5728\u5B89\u88C5\u63D2\u4EF6\u65F6\u4E00\u5E76\u88C5\u597D(\u65E0\u9700 C++ \u5DE5\u5177\u94FE)\u3002\u82E5\u6B64\u5904\u957F\u671F\u672A\u8FD0\u884C,\u8BF7\u67E5\u770B DSH host \u65E5\u5FD7\u91CC\u7684 [code-server] \u8F93\u51FA(\u6216\u5728\u8BBE\u7F6E\u5361\u7247\u91CC\u5207\u6362\u300C\u540E\u53F0\u5E38\u9A7B\u300D\u89E6\u53D1\u4E00\u6B21\u542F\u52A8)\u3002"+h+w)))}function Je(e){var r=V(),n=r.status,t=n!=null&&n.ok===!0&&n.running===!0,l=n!=null&&n.keepResident===!0,s=me(e&&e.useSessions,e&&e.useWorkspaces),c=t?be(n,s):null;return i.useEffect(function(){!l||!t||c===null||Y({src:c,sameOrigin:n!=null&&n.serve==="dsh"})},[l,t,c,n!=null?n.serve:null]),null}var Xe=".dshcs-frame{display:block;position:absolute;inset:0;z-index:1;width:100%;height:100%;border:0;background:var(--dsw-alias-bg-base,#fff)}.dshcs-tabroot{position:relative;display:flex;flex-direction:column;width:100%;height:100%;min-width:0;min-height:0;background:var(--dsw-alias-bg-base,#fff)}.dshcs-slot{position:relative;display:block;width:100%;height:100%;min-width:0;min-height:0;overflow:hidden;background:var(--dsw-alias-bg-base,#fff)}.dshcs-park{position:fixed;left:-20000px;top:0;width:320px;height:200px;overflow:hidden;pointer-events:none;z-index:0;visibility:hidden;contain:strict}.dshcs-loading{position:absolute;inset:0;z-index:2;display:grid;place-items:center;background:var(--dsw-alias-bg-base,#fff);color:var(--dsw-alias-label-tertiary,#7d8798);font-size:13px}.dshcs-empty{position:relative;z-index:2;flex:1;display:flex;align-items:center;justify-content:center;padding:44px 24px 24px}.dshcs-emptybox{max-width:560px;text-align:left;background:var(--dsw-alias-bg-layer-2,#f7f8fb);border:1px solid var(--dsw-alias-border-l2,#dfe3eb);border-radius:12px;padding:16px 18px}.dshcs-error{color:var(--dsw-alias-label-error,#d92d20);white-space:pre-wrap;word-break:break-all;font-family:ui-monospace,Consolas,monospace;font-size:12px;margin:8px 0 0}.dshcs-hint{color:var(--dsw-alias-label-tertiary,#7d8798);font-size:12px;margin:6px 0 0}.dshcs-legacy{color:var(--dsw-alias-label-warning,#b54708);font-size:12px;font-weight:600;margin:0 0 6px}",de="dsh-code-server/styles";typeof document!="undefined"&&document.querySelector("style[data-dshcs="+JSON.stringify(de)+"]")===null&&(U=document.createElement("style"),U.setAttribute("data-dshcs",de),U.textContent=Xe,document.head.appendChild(U));var U,ue="code-server",fe="dsh-code-server-app",Ve=2500,qe=1e4,Ke=1500;function $e(e){var r=e!=null&&typeof e.size=="number"?e.size:16;return i.createElement("img",{src:Ye,alt:"","aria-hidden":!0,draggable:!1,className:e!=null?e.className:void 0,style:{width:r,height:r,display:"block",objectFit:"contain",WebkitUserDrag:"none",userSelect:"none"}})}var er=P("");function rr(){var e=j.status;return e==null||typeof e.claimExtensions!="string"?er:P(e.claimExtensions)}function nr(e,r){try{var n=e(function(l){return l});if(n!=null&&r!=null&&n.byId!=null){var t=n.byId[r];if(t!=null&&typeof t.cwd=="string"&&t.cwd!=="")return t.cwd}}catch(l){}}function tr(e){var r=e.useTabInfo(),n=r.tab,t=V(),l=t.status,s=n.navigation,c=s!=null&&typeof s.revision=="number"?s.revision:0,u=s!=null&&typeof s.address=="string"?s.address:"",p=F(u)?null:D(u),o=nr(e.useSessions,p!=null?p.sessionId:void 0),f=o!==void 0?o:me(e.useSessions,null),h=re(p,f),w=s!=null&&s.params!=null&&typeof s.params.line=="number"?s.params.line:null,d=i.useRef(void 0),[g,v]=i.useState(0),x=i.useRef(null),L=i.useRef(!1),z=r.tab!=null&&r.tab.visible===!0,N=r.sidebar!=null&&r.sidebar.fullscreen===!0,_=l!=null&&l.fullscreenOnOpen!==!1;return i.useLayoutEffect(function(){if(!z){L.current=!1;return}if(!(!_||N||L.current)){L.current=!0;var E=ae(x.current);E!=="clicked"&&console.warn("[code-server] \u672A\u80FD\u81EA\u52A8\u5207\u5230\u5168\u5C4F\u53F3\u4FA7\u680F("+E+");\u672C\u6B21\u6253\u5F00\u4FDD\u6301\u539F\u6A21\u5F0F")}},[z,N,_]),i.useEffect(function(){if(!(typeof f!="string"||f==="")&&d.current!==f){var E=d.current!==void 0;d.current=f;var b=!1;return k("/code-server/start",{cwd:f}).then(function(O){b||(S({status:O}),E&&O!=null&&O.ok===!0&&O.running===!0&&v(function(A){return A+1}))}).catch(function(){}),function(){b=!0}}},[f]),i.useEffect(function(){k("/code-server/status").then(function(b){b!=null&&S({status:b})}).catch(function(){});var E=window.setInterval(function(){k("/code-server/status").then(function(b){S({status:b})}).catch(function(){})},3e3);return function(){window.clearInterval(E)}},[]),i.useEffect(function(){h!==null&&k("/code-server/open-file",w===null?{file:h}:{file:h,line:w}).catch(function(){})},[c,h,w]),i.createElement("div",{className:"dshcs-tabroot","data-code-server-tab":"body",ref:x},i.createElement(Ze,{status:l,pageUrl:be(l,f),reloadTick:g,owner:"tab:"+n.id}))}function Z(e,r){if(e==null)return null;var n=e[r];if(n!=null)return n;if(typeof e.get=="function"){var t=e.get(r);if(t!=null)return t}return null}function ar(e){var r=Z(e,"sidebarRightTabs"),n=Z(e,"sidebarRight");if(r==null||n==null)return console.error("[code-server] \u53F3\u4FA7\u680F\u670D\u52A1\u4E0A\u4E0B\u6587\u4E0D\u53EF\u89C1(sidebarRightTabs="+(r!=null)+", sidebarRight="+(n!=null)+"):\u8DF3\u8FC7\u4FA7\u680F\u6CE8\u518C"),!1;var t=Z(e,"slots");if(e.effect(function(){return r.register({id:fe,kind:ue,priority:"extension",patterns:["dsh-resource://file/**"],canOpen:function(l){return ne(D(l),rr())},title:function(l){if(F(l)||l===void 0||l===null||l==="")return"Code Server";var s=ee(l);return s===""?"Code Server":s},guide:[{order:20,title:function(){return"Code Server"},description:function(){return"\u5728\u53F3\u4FA7\u680F\u6807\u7B7E\u91CC\u8FD0\u884C VS Code \u7F51\u9875\u7248,\u8DDF\u968F\u5F53\u524D\u4F1A\u8BDD\u5DE5\u4F5C\u533A;\u4EA7\u7269/\u4EA4\u4ED8\u6587\u4EF6\u70B9\u51FB\u540E\u5728\u6B64\u6253\u5F00\u3002"},icon:$e}]})},"code-server: sidebar tab type"),t!=null)e.effect(function(){return t.inject("sidebar.right.pane.tab",function(){return t.register({name:"sidebar.right.pane.tab",key:fe},tr)})},"code-server: sidebar tab body");else return console.error("[code-server] slots \u670D\u52A1\u4E0D\u53EF\u89C1:\u4FA7\u680F\u6807\u7B7E body \u672A\u6CE8\u518C"),!1;return e.effect(function(){return function(){S({sidebarUi:"unknown"})}},"code-server: sidebar mode reset"),console.log("[code-server] right-sidebar tab registered (kind="+ue+")"),!0}var lr=".dshcs-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.dshcs-card:hover{border-color:var(--dsw-alias-label-dimmed,var(--dsw-alias-border-l2))}.dshcs-cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed,var(--dsw-alias-border-l2))}.dshcs-cardHeader{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.dshcs-cardHeader:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.dshcs-cardHeadText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.dshcs-cardName{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.dshcs-cardDescription{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));font-size:13px;line-height:1.5}.dshcs-cardChevron{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));flex:none;transition:transform .16s;display:inline-flex}.dshcs-cardChevronOpen{transform:rotate(180deg)}.dshcs-cardBody{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.dshcs-cardReadOnly{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));margin:12px 0 0;font-size:12px;line-height:1.5}.dshcs-pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.dshcs-cardFooter{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.dshcs-cardFailed{min-width:0;color:var(--dsw-alias-label-error,var(--dsw-alias-state-error-primary));flex:1;margin:0;font-size:12px;line-height:1.5}.dshcs-cbtn{appearance:none;font:inherit;cursor:pointer;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5;border:1px solid transparent;transition:color .12s ease,background .12s ease}.dshcs-cbtn:disabled{opacity:.4;cursor:default}.dshcs-cbtn:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.dshcs-cbtnOutline{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.dshcs-cbtnOutline:hover:not(:disabled){color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover,transparent)}.dshcs-cbtnPrimary{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3);border-color:var(--dsw-alias-label-primary);font-weight:600}.dshcs-cbtnPrimary:hover:not(:disabled){filter:brightness(.95)}.dshcs-cbtnPrimary:active:not(:disabled){filter:brightness(.9)}.dshcs-field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.dshcs-field+.dshcs-field{border-top:1px solid var(--dsw-alias-border-l2)}.dshcs-fieldHead{align-items:center;gap:8px;display:flex}.dshcs-fieldLabel{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.dshcs-card .dshcs-hint{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));margin:0;font-size:12px;line-height:1.5}.dshcs-check{display:flex;align-items:center;gap:8px;cursor:pointer}.dshcs-check input{accent-color:var(--dsw-alias-brand-primary);width:15px;height:15px;margin:0;flex:none}.dshcs-check input:disabled{cursor:default}.dshcs-text{display:flex}.dshcs-text input{font:12px/1.5 ui-monospace,Consolas,monospace;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3,transparent);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:5px 8px;width:100%;min-width:0}.dshcs-text input:disabled{opacity:.5;cursor:default}.dshcs-text input:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-1px}.dshcs-badges{align-items:center;gap:8px;display:inline-flex}.dshcs-badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.dshcs-reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.dshcs-reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.dshcs-reset:disabled{cursor:default}",ir="dsh-code-server/Card.module.css";function sr(e,r){if(typeof document!="undefined"&&document.querySelector("style[data-plugin-css="+JSON.stringify(e)+"]")===null){var n=document.createElement("style");n.dataset.plugin="dsh-code-server",n.dataset.pluginCss=e,n.textContent=r,document.head.appendChild(n)}}sr(ir,lr);function cr(e){return i.createElement("svg",{width:14,height:14,viewBox:"0 0 14 14",fill:"none","aria-hidden":!0,className:e.className},i.createElement("path",{d:"M3.5 5.25L7 8.75L10.5 5.25",stroke:"currentColor",strokeWidth:1.5,strokeLinecap:"round",strokeLinejoin:"round"}))}function pe(e){return i.createElement("button",{type:"button",className:"dshcs-cbtn "+(e.variant==="primary"?"dshcs-cbtnPrimary":"dshcs-cbtnOutline"),disabled:e.disabled===!0,onClick:e.onClick},e.children)}function ve(e){return i.createElement("label",{className:"dshcs-check"},i.createElement("input",{type:"checkbox",checked:e.checked===!0,disabled:e.disabled===!0,onChange:function(r){e.onChange(r.target.checked)}}),i.createElement("span",{className:"dshcs-hint"},e.children))}function or(e){return i.createElement("label",{className:"dshcs-text"},i.createElement("input",{type:"text",value:e.value,disabled:e.disabled===!0,spellCheck:!1,autoComplete:"off",placeholder:e.placeholder,onChange:function(r){e.onChange(r.target.value)}}))}function J(e){var r=[];return e.overridden===!0&&(r.push(i.createElement("span",{className:"dshcs-badge"},e.overriddenLabel)),r.push(i.createElement("button",{type:"button",className:"dshcs-reset",disabled:e.disabled===!0,onClick:e.onReset},e.resetLabel))),r.length===0?null:i.createElement("span",{className:"dshcs-badges"},...r)}function he(e){var[r,n]=i.useState(e.defaultOpen===!0),t=e.state;if(t==null||t.available!==!0)return null;var l=e.title,s=e.noticeOnly===!0,c=t.dirty!==!0||t.invalid===!0||t.saving===!0;return i.createElement("li",{className:"dshcs-card"+(r?" dshcs-cardOpen":"")},i.createElement("button",{type:"button",className:"dshcs-cardHeader","aria-expanded":r,onClick:function(){n(!r)}},i.createElement("span",{className:"dshcs-cardHeadText"},i.createElement("span",{className:"dshcs-cardName"},l),i.createElement("span",{className:"dshcs-cardDescription"},e.description)),t.dirty===!0?i.createElement("span",{className:"dshcs-pending"},e.unsavedLabel):null,i.createElement(cr,{className:"dshcs-cardChevron"+(r?" dshcs-cardChevronOpen":"")})),r===!0?i.createElement("div",{className:"dshcs-cardBody"},s===!0?null:t.writable!==!0?i.createElement("p",{className:"dshcs-cardReadOnly",role:"status"},e.readOnlyLabel):null,e.children,s===!0?null:i.createElement("div",{className:"dshcs-cardFooter"},t.failed===!0?i.createElement("p",{className:"dshcs-cardFailed",role:"status"},e.saveFailedLabel):null,i.createElement(pe,{disabled:t.dirty!==!0||t.saving===!0,onClick:e.onDiscard},e.discardLabel),i.createElement(pe,{variant:"primary",disabled:c,onClick:e.onSave},t.saving===!0?e.savingLabel:e.saveLabel))):null)}function dr(e){try{let b=function(){k("/code-server/status").then(function(m){m!=null&&typeof m=="object"&&S({status:m})}).catch(function(){})};var r=e.scope,[n,t]=i.useState(function(){return r!==void 0?r.getSnapshot():null}),[l,s]=i.useState(null),[c,u]=i.useState(!1),[p,o]=i.useState(!1),f=V(),[h,w]=i.useState(ie());if(i.useEffect(function(){return le(function(m){w(m)})},[]),i.useEffect(function(){if(r===void 0||typeof r.subscribe!="function")return;function m(){try{t(r.getSnapshot())}catch(mr){}}var y=r.subscribe(m);return function(){typeof y=="function"&&y()}},[r]),r===void 0)return console.error("[code-server] card scope missing"),null;if(n==null||n.status!=="ready")return n!=null&&n.status==="unavailable"&&console.warn("[code-server] settings scope unavailable:",n.status),null;var d=n.value!==void 0&&n.value!==null?n.value:{},g=n.user,v={keepResident:d.keepResident!==!1,claimExtensions:typeof d.claimExtensions=="string"?d.claimExtensions:T,fullscreenOnOpen:d.fullscreenOnOpen!==!1};if(f!=null&&f.sidebarUi==="legacy")return i.createElement(he,{title:"Code Server",description:"\u5F53\u524D DSH \u7248\u672C\u4E0D\u53D7\u652F\u6301(\u7F3A\u5C11\u53F3\u4FA7\u680F\u670D\u52A1)",defaultOpen:!0,noticeOnly:!0,state:{available:!0,writable:!1,dirty:!1,invalid:!1,saving:!1,failed:!1}},i.createElement("div",{className:"dshcs-field"},i.createElement("div",{className:"dshcs-legacy"},"\u672C\u63D2\u4EF6\u81EA 0.2.3 \u8D77\u4E0D\u518D\u517C\u5BB9\u65E7\u7248 DSH\u3002"),i.createElement("div",{className:"dshcs-hint",style:{marginTop:0}},`\u672A\u68C0\u6D4B\u5230\u53F3\u4FA7\u680F\u63D2\u4EF6\u670D\u52A1 sidebarRightTabs / sidebarRight,\u56E0\u6B64\u63D2\u4EF6\u4E0D\u63D0\u4F9B\u4EFB\u4F55\u5165\u53E3(\u65E7\u7248\u7684\u60AC\u6D6E\u7403\u4E0E\u6D6E\u52A8\u7A97\u53E3\u5DF2\u79FB\u9664),\u4E5F\u4E0D\u4F1A\u540E\u53F0\u542F\u52A8 IDE\u3002
3
3
  \u5347\u7EA7 DSH \u5230\u5E26\u53F3\u4FA7\u680F\u7684\u7248\u672C(\u2265 0.1.5-alpha.1)\u540E,Code Server \u4F1A\u51FA\u73B0\u5728\u53F3\u4FA7\u680F\u6807\u7B7E\u91CC,\u672C\u9875\u540C\u65F6\u663E\u793A\u5B8C\u6574\u8BBE\u7F6E\u9879;\u5347\u7EA7\u540E\u65E0\u9700\u91CD\u88C5\u672C\u63D2\u4EF6,\u5237\u65B0\u9875\u9762\u5373\u53EF\u3002`)));var x=g!=null&&Object.prototype.hasOwnProperty.call(g,"keepResident"),L=g!=null&&Object.prototype.hasOwnProperty.call(g,"claimExtensions"),z=g!=null&&Object.prototype.hasOwnProperty.call(g,"fullscreenOnOpen"),N=l!==null&&(l.keepResident!==v.keepResident||C(l.claimExtensions)!==C(v.claimExtensions)||l.fullscreenOnOpen!==v.fullscreenOnOpen),_=!N||c,E={available:!0,writable:n.writable,dirty:N,invalid:!1,saving:c,failed:p};async function O(){if(N&&!c){u(!0),o(!1);try{var m=l!==null?l:v;await e.scope.set("keepResident",m.keepResident===!0),await e.scope.set("claimExtensions",C(m.claimExtensions)),await e.scope.set("fullscreenOnOpen",m.fullscreenOnOpen===!0),s(null),b()}catch(y){o(!0)}u(!1)}}async function A(){if(!(c||n.writable!==!0)){u(!0),o(!1);try{typeof e.scope.unset=="function"?(await e.scope.unset("keepResident"),await e.scope.unset("claimExtensions"),await e.scope.unset("fullscreenOnOpen")):(await e.scope.set("keepResident",d.keepResident===void 0||d.keepResident===null?!0:d.keepResident),await e.scope.set("claimExtensions",typeof d.claimExtensions=="string"?C(d.claimExtensions):T),await e.scope.set("fullscreenOnOpen",d.fullscreenOnOpen===void 0||d.fullscreenOnOpen===null?!0:d.fullscreenOnOpen)),s(null),b()}catch(m){o(!0)}u(!1)}}return i.createElement(he,{title:"Code Server",description:"\u5165\u53E3:\u53F3\u4FA7\u680F\u6807\u7B7E(DSH \u2265 0.1.5-alpha.1);\u8BA4\u9886\u7C7B\u578B\u51B3\u5B9A\u54EA\u4E9B\u6587\u4EF6\u4EA4\u7ED9 Code Server \u6253\u5F00",state:E,unsavedLabel:"\u672A\u4FDD\u5B58",readOnlyLabel:"\u672C\u90E8\u7F72\u7684\u8BBE\u7F6E\u4E3A\u53EA\u8BFB\u3002",saveFailedLabel:"\u672C\u90E8\u7F72\u6CA1\u6709\u63A5\u53D7\u8FD9\u4E9B\u503C\uFF0C\u5DF2\u4FDD\u7559\u4F9B\u4F60\u4FEE\u6539\u3002",discardLabel:"\u653E\u5F03\u4FEE\u6539",saveLabel:"\u4FDD\u5B58",savingLabel:"\u4FDD\u5B58\u4E2D\u2026",onSave:O,onDiscard:function(){s(null),o(!1)}},i.createElement("div",{className:"dshcs-field"},i.createElement("div",{className:"dshcs-fieldHead"},i.createElement("span",{className:"dshcs-fieldLabel"},"\u8BA4\u9886\u7C7B\u578B(\u6309\u6269\u5C55\u540D,\u5206\u53F7\u5206\u9694)"),L===!0?i.createElement(J,{overridden:!0,disabled:n.writable!==!0,overriddenLabel:"\u5DF2\u8986\u76D6",resetLabel:"\u6062\u590D\u9ED8\u8BA4",onReset:function(){A()}}):null),i.createElement(or,{value:l!==null?l.claimExtensions:v.claimExtensions,disabled:n.writable!==!0,placeholder:T,onChange:function(m){s(function(y){return Object.assign({},y!==null?y:v,{claimExtensions:m})}),o(!1)}}),i.createElement("div",{className:"dshcs-hint",style:{marginTop:4}},"\u5199\u6269\u5C55\u540D(\u4E0D\u5E26\u70B9,\u5927\u5C0F\u5199\u968F\u610F);`*` = \u5176\u4F59\u7C7B\u578B\u4E5F\u8BA4\u9886;`!ext` = \u4E0D\u8BA4\u9886(\u6392\u9664\u4F18\u5148)\u3002\u4E0D\u518D\u533A\u5206\u4F1A\u8BDD\u5185/\u7EDD\u5BF9\u8DEF\u5F84 \u2014\u2014 \u6240\u6709 `dsh-resource://file/**` \u4E00\u89C6\u540C\u4EC1,\u672A\u8BA4\u9886\u7684\u843D\u56DE DSH \u81EA\u5E26\u9884\u89C8\u3002"),i.createElement("div",{className:"dshcs-hint",style:{marginTop:2}},"\u5B9E\u9645\u89C4\u5219:"+te(l!==null?l.claimExtensions:v.claimExtensions)+(C(l!==null?l.claimExtensions:v.claimExtensions)!==(l!==null?l.claimExtensions:v.claimExtensions)?"(\u4FDD\u5B58\u540E\u5F52\u4E00\u5316)":"")+" \xB7 \u9ED8\u8BA4 "+T)),i.createElement("div",{className:"dshcs-field"},i.createElement("div",{className:"dshcs-fieldHead"},i.createElement("span",{className:"dshcs-fieldLabel"},"\u6253\u5F00\u5373\u5168\u5C4F(\u53F3\u4FA7\u680F\u94FA\u6EE1\u7A97\u53E3)"),z===!0?i.createElement(J,{overridden:!0,disabled:n.writable!==!0,overriddenLabel:"\u5DF2\u8986\u76D6",resetLabel:"\u6062\u590D\u9ED8\u8BA4",onReset:function(){A()}}):null),i.createElement(ve,{checked:l!==null?l.fullscreenOnOpen:v.fullscreenOnOpen,disabled:n.writable!==!0,onChange:function(m){s(function(y){return Object.assign({},y!==null?y:v,{fullscreenOnOpen:m===!0})}),o(!1)}},'\u6253\u5F00 Code Server \u6807\u7B7E(\u542B\u70B9\u5F00\u4EA7\u7269/\u4EA4\u4ED8\u6587\u4EF6)\u65F6,\u81EA\u52A8\u628A\u53F3\u4FA7\u680F\u4ECE"\u4E0E\u5BF9\u8BDD\u5E76\u6392"\u5207\u5230\u5168\u5C4F;\u60F3\u540C\u65F6\u770B\u5BF9\u8BDD\u5C31\u5173\u6389\u5B83,\u6216\u968F\u65F6\u70B9\u53F3\u4FA7\u680F\u7684\u300C\u9000\u51FA\u5168\u5C4F\u300D\u2014\u2014 \u672C\u6B21\u6253\u5F00\u4E0D\u4F1A\u88AB\u62A2\u56DE\u53BB'),i.createElement("div",{className:"dshcs-hint",style:{marginTop:4}},'\u53EA\u5F71\u54CD"\u6253\u5F00\u90A3\u4E00\u523B":\u5207\u8D70\u518D\u5207\u56DE\u3001\u518D\u6B21\u6253\u5F00\u6587\u4EF6 tab \u4F1A\u91CD\u65B0\u5207\u5168\u5C4F;DSH \u672A\u5411\u63D2\u4EF6\u5F00\u653E\u6A21\u5F0F\u63A5\u53E3,\u672C\u9879\u7531\u70B9\u51FB\u53F3\u4FA7\u680F\u81EA\u8EAB\u7684\u300C\u5168\u5C4F\u300D\u6309\u94AE\u5B9E\u73B0\u3002')),i.createElement("div",{className:"dshcs-field"},i.createElement("div",{className:"dshcs-fieldHead"},i.createElement("span",{className:"dshcs-fieldLabel"},"\u540E\u53F0\u5E38\u9A7B(\u5207\u6807\u7B7E\u4E0D\u91CD\u8F7D)"),x===!0?i.createElement(J,{overridden:!0,disabled:n.writable!==!0,overriddenLabel:"\u5DF2\u8986\u76D6",resetLabel:"\u6062\u590D\u9ED8\u8BA4",onReset:function(){A()}}):null),i.createElement(ve,{checked:l!==null?l.keepResident:v.keepResident,disabled:n.writable!==!0,onChange:function(m){s(function(y){return Object.assign({},y!==null?y:v,{keepResident:m===!0})}),o(!1)}},'\u5F00\u542F\u540E\u5BBF\u4E3B\u542F\u52A8\u5373\u628A IDE \u52A0\u8F7D\u5230\u540E\u53F0"\u505C\u653E\u533A":\u5207\u6362\u53F3\u4FA7\u680F\u6807\u7B7E\u3001\u6536\u8D77/\u5C55\u5F00\u4FA7\u680F\u3001\u62D6\u6210\u6D6E\u52A8\u7A97\u53E3\u90FD\u4E0D\u518D\u91CD\u8F7D,\u9996\u6B21\u6253\u5F00\u514D\u7B49\u5F85;\u5173\u95ED\u5219\u53EA\u5728\u6253\u5F00\u9762\u677F\u65F6\u52A0\u8F7D(\u7701\u5185\u5B58)'),i.createElement("div",{className:"dshcs-hint",style:{marginTop:4}},R?"\u5E38\u9A7B\u9762:"+(h.docked?"\u5DF2\u505C\u9760":h.ready?"\u5DF2\u505C\u653E(\u540E\u53F0\u8FD0\u884C\u4E2D)":"\u672A\u542F\u52A8")+(h.degraded?" \xB7 \u672C\u6B21\u53D1\u751F\u8FC7\u964D\u7EA7\u91CD\u8F7D":""):"\u5F53\u524D\u6D4F\u89C8\u5668\u4E0D\u652F\u6301 Element.moveBefore(Chromium <133)\u2192 \u5E38\u9A7B\u4E0D\u53EF\u7528,\u5207\u6807\u7B7E\u4ECD\u4F1A\u6574\u9875\u91CD\u8F7D(\u5347\u7EA7\u6D4F\u89C8\u5668\u540E\u81EA\u52A8\u751F\u6548)")))}catch(b){return console.error("[code-server] card render error:",b!=null&&b.message!==void 0?b.message:String(b)),null}}function ur(e){try{fr(e)}catch(r){console.error("[code-server] apply failed:",r&&r.stack?r.stack:String(r));try{document.title="CS-ERR "+(r&&r.message||String(r))}catch(n){}}}function fr(e){var r=e.get("slots");if(r===void 0){console.error("[code-server] slots service unavailable");return}var n=e.settingsScope,t=null;if(n!==void 0&&typeof n.bind=="function")t=n.bind({namespace:"code-server"});else{console.error("[code-server] settingsScope unavailable (inject missing?); settings card disabled");return}k("/code-server/status").then(function(d){S({status:d})}).catch(function(){}),r.inject("settings.plugin.item",()=>r.register({name:"settings.plugin.item",key:"code-server",label:"Code Server",inject:function(){return{scope:t}}},dr));var l=!1;function s(d,g){if(!l){l=!0,f();var v=!1;try{v=ar(d)}catch(x){console.error("[code-server] sidebar tab registration failed:",x!=null&&x.message!=null?x.message:String(x))}j.sidebarUi==="legacy"&&(console.log("[code-server] \u53F3\u4FA7\u680F\u670D\u52A1\u665A\u5230(\u7ECF "+g+"):\u64A4\u9500\u65E7\u7248 DSH \u5224\u5B9A"),k("/code-server/ui-mode",{sidebar:!0}).catch(function(){})),S({sidebarUi:"modern",sidebarRegisterFailed:v!==!0}),r.inject("shell.overlay",()=>r.register({name:"shell.overlay",id:"code-server",order:70,label:"Code Server"},x=>i.createElement(Je,x))),console.log("[code-server] client registered via "+g+": sidebar tab="+v+"(claims dsh-resource://file/**) + resident preload + settings card")}}function c(d){S({sidebarUi:"legacy"}),console.warn("[code-server] \u672A\u63A2\u6D4B\u5230\u53F3\u4FA7\u680F\u670D\u52A1(sidebarRightTabs/sidebarRight): \u672C\u63D2\u4EF6\u81EA 0.2.3 \u8D77\u4E0D\u518D\u517C\u5BB9\u65E7\u7248 DSH \u2014\u2014 \u9664\u300C\u8BBE\u7F6E \u2192 \u63D2\u4EF6 \u2192 Code Server\u300D\u7684\u63D0\u793A\u5916\u4E0D\u63D0\u4F9B\u4EFB\u4F55\u5165\u53E3,\u4E5F\u4E0D\u9884\u70ED IDE\u3002\u8BF7\u5347\u7EA7 DSH\u3002"),d===!0&&k("/code-server/ui-mode",{sidebar:!1}).catch(function(){})}var u=null,p=null,o=null;function f(){u!==null&&(clearTimeout(u),u=null),p!==null&&(clearTimeout(p),p=null),o!==null&&(clearTimeout(o),o=null)}var h=!1;try{typeof e.inject=="function"&&(h=!0,e.inject(["sidebarRightTabs","sidebarRight"],function(d){s(d,"inject")}))}catch(d){console.warn("[code-server] sidebar inject failed:",d!=null&&d.message!=null?d.message:String(d))}var w=typeof e.get=="function"&&e.get("sidebarRightTabs")!==void 0&&e.get("sidebarRight")!==void 0;if(w){o=setTimeout(function(){l||(console.warn("[code-server] ctx.inject \u672A\u5728 1.5s \u5185\u56DE\u8C03,\u6539\u7528\u540C\u6B65\u670D\u52A1\u6CE8\u518C\u4FA7\u680F"),s(e,"sync-fallback"))},Ke);return}if(h!==!0){c(!0);return}u=setTimeout(function(){l||c(!1)},Ve),p=setTimeout(function(){l||c(!0)},qe)}var pr=["slots","settingsScope"],vr="code-server";
4
4
  return module.exports;}});
5
5
 
package/lib/index.js CHANGED
@@ -17,6 +17,7 @@
17
17
  */
18
18
 
19
19
  import { spawn, execFile, execFileSync } from 'node:child_process';
20
+ import { randomBytes } from 'node:crypto';
20
21
  import * as fs from 'node:fs';
21
22
  import * as path from 'node:path';
22
23
  import * as os from 'node:os';
@@ -92,7 +93,9 @@ export const Config = z.object({
92
93
  const DEFAULT_CONFIG = {
93
94
  bin: '',
94
95
  host: '127.0.0.1',
95
- port: 8090,
96
+ /** 0(默认)= 每次启动由系统分配空闲端口(launcher 把实际端口写进 endpoint 文件,host 读回);
97
+ * 显式给端口则照用(用于需要固定地址的场景)。 */
98
+ port: 0,
96
99
  auth: 'none',
97
100
  serve: 'loopback',
98
101
  userDataDir: '',
@@ -103,6 +106,8 @@ const DEFAULT_CONFIG = {
103
106
 
104
107
  const LOOPBACK_HOSTS = new Set(['127.0.0.1', 'localhost', '::1']);
105
108
  const LOG_TAIL_MAX = 6000;
109
+ /** 路径令牌格式(与 launcher 的一致;字符集同时满足 VS Code 自己的 token 校验)。 */
110
+ const TOKEN_RE = /^[0-9A-Za-z_-]{16,128}$/;
106
111
 
107
112
  function dshHome() {
108
113
  return process.env.DSH_HOME || path.join(os.homedir(), '.dsh');
@@ -116,6 +121,16 @@ function pidFile(config) {
116
121
  return path.join(dataRoot(config), 'pid.json');
117
122
  }
118
123
 
124
+ /** 路径令牌文件(0600 语义:位于用户 profile 下,默认 ACL 仅本人可读)。 */
125
+ function tokenFile(config) {
126
+ return path.join(dataRoot(config), 'path-token');
127
+ }
128
+
129
+ /** launcher 回写实际监听地址的文件(端口 0 时 host 只能从这里知道端口)。 */
130
+ function endpointFile(config) {
131
+ return path.join(dataRoot(config), 'endpoint.json');
132
+ }
133
+
119
134
  function isAlive(pid) {
120
135
  if (!Number.isInteger(pid) || pid <= 0) return false;
121
136
  try {
@@ -315,9 +330,10 @@ function pipeName() {
315
330
  return win32() ? `\\\\.\\pipe\\dshcs-vscode-${key}` : path.join(os.tmpdir(), `dshcs-vscode-${key}.sock`);
316
331
  }
317
332
 
318
- function healthCheck(host, port, timeoutMs = 1500) {
333
+ function healthCheck(host, port, token, timeoutMs = 1500) {
334
+ const requestPath = token === null || token === undefined ? '/healthz' : `/${token}/healthz`;
319
335
  return new Promise((resolve) => {
320
- const req = http.get({ host, port, path: '/healthz', timeout: timeoutMs, method: 'GET' }, (res) => {
336
+ const req = http.get({ host, port, path: requestPath, timeout: timeoutMs, method: 'GET' }, (res) => {
321
337
  res.resume();
322
338
  resolve({ ok: res.statusCode >= 200 && res.statusCode < 500, statusCode: res.statusCode });
323
339
  });
@@ -367,6 +383,37 @@ function removePidFile(config) {
367
383
  }
368
384
  }
369
385
 
386
+ /** 读路径令牌;文件缺失或格式不对返回 null(不抛:调用方据此判定"没有可接管的实例")。 */
387
+ function readToken(config) {
388
+ try {
389
+ const raw = fs.readFileSync(tokenFile(config), 'utf8').trim();
390
+ return TOKEN_RE.test(raw) ? raw : null;
391
+ } catch {
392
+ return null;
393
+ }
394
+ }
395
+
396
+ /** 生成并写入新的路径令牌(每次**新启动**都换;原子写,0600)。 */
397
+ function rotateToken(config) {
398
+ const token = randomBytes(24).toString('base64url'); // [A-Za-z0-9_-] 共 32 位
399
+ const file = tokenFile(config);
400
+ fs.mkdirSync(path.dirname(file), { recursive: true });
401
+ const tmp = `${file}.${process.pid}.tmp`;
402
+ fs.writeFileSync(tmp, token, { encoding: 'utf8', mode: 0o600 });
403
+ fs.renameSync(tmp, file);
404
+ return token;
405
+ }
406
+
407
+ /** 读 launcher 回写的监听端点(端口 0 时唯一可信来源);缺失/不可用返回 null。 */
408
+ function readEndpoint(config) {
409
+ try {
410
+ const raw = JSON.parse(fs.readFileSync(endpointFile(config), 'utf8'));
411
+ return raw && Number.isInteger(raw.pid) && Number.isInteger(raw.port) && raw.port > 0 ? raw : null;
412
+ } catch {
413
+ return null;
414
+ }
415
+ }
416
+
370
417
  export async function apply(ctx, config) {
371
418
  const cfg = { ...DEFAULT_CONFIG, ...(config ?? {}) };
372
419
 
@@ -448,6 +495,8 @@ export async function apply(ctx, config) {
448
495
  status: 'stopped', // stopped | starting | running | stopping | error
449
496
  pid: null,
450
497
  port: cfg.port,
498
+ /** 本实例的路径令牌(回环模式;不写日志、不进 argv)。 */
499
+ token: null,
451
500
  cwd: null,
452
501
  /** 进程**启动时**的工作目录(诊断用):运行时切工作区只改 cwd,进程 cwd 保持这里不变。 */
453
502
  launchCwd: null,
@@ -497,6 +546,13 @@ export async function apply(ctx, config) {
497
546
  }
498
547
  }, 'code-server: dsh mount');
499
548
 
549
+ /** 回环模式的客户端 URL:随机端口 + 路径令牌(令牌是 URL 路径的一段,浏览器会把子请求与 WS
550
+ * 一并带过去 —— 这正是不走 VS Code 自带 cookie 令牌的原因,见 launcher 文件头"安全模型")。 */
551
+ function loopbackUrl() {
552
+ const token = typeof state.token === 'string' && state.token !== '' ? `${state.token}/` : '';
553
+ return `http://${cfg.host}:${state.port}/${token}`;
554
+ }
555
+
500
556
  function snapshot() {
501
557
  const running = state.status === 'running' && state.pid !== null;
502
558
  const dshMode = state.serve === 'dsh';
@@ -510,8 +566,9 @@ export async function apply(ctx, config) {
510
566
  pid: state.pid,
511
567
  cwd: state.cwd,
512
568
  launchCwd: state.launchCwd,
513
- // dsh 模式:同源相对地址(由 DSH webServer 的 prefix 路由提供服务)
514
- url: running ? (dshMode ? '/code-server/' : `http://${cfg.host}:${state.port}/`) : null,
569
+ // dsh 模式:同源相对地址(由 DSH webServer 的 prefix 路由提供服务);
570
+ // loopback:随机端口 + 路径令牌(令牌只出现在这个 URL 里,客户端据此加载 iframe)
571
+ url: running ? (dshMode ? '/code-server/' : loopbackUrl()) : null,
515
572
  productPath: state.env?.productPath ?? null,
516
573
  version: state.version,
517
574
  error: state.error,
@@ -621,15 +678,17 @@ export async function apply(ctx, config) {
621
678
  state.pid = null;
622
679
  state.cwd = null;
623
680
  state.launchCwd = null;
681
+ state.token = null;
682
+ state.port = cfg.port;
624
683
  state.startedAt = null;
625
684
  state.adopted = false;
626
685
  if (reason) state.error = null;
627
686
  }
628
687
 
629
- /** 就绪探针:loopback 走 TCP,dsh 模式走命名管道。 */
688
+ /** 就绪探针:loopback 走 TCP(带路径令牌),dsh 模式走命名管道。 */
630
689
  function probeReady(timeoutMs = 1500) {
631
690
  if (state.pipe !== null) return healthCheckPipe(state.pipe, timeoutMs);
632
- return healthCheck(cfg.host, state.port, timeoutMs);
691
+ return healthCheck(cfg.host, state.port, state.token, timeoutMs);
633
692
  }
634
693
 
635
694
  function beginPollingReady() {
@@ -720,15 +779,20 @@ export async function apply(ctx, config) {
720
779
  console.warn('[code-server] 0.2.0 起不再支持口令认证(argon2 已移除),按 auth=none 运行');
721
780
  }
722
781
 
723
- // 端口占用则尝试 adopt(仅 loopback 模式;pid.json 有效 + /healthz 响应),否则报错
724
- if (serve === 'loopback') {
725
- const probe = await healthCheck(cfg.host, cfg.port, 800);
782
+ // 端口占用则尝试 adopt(仅"显式指定端口"时;默认 port=0 由系统分配,不存在占用问题)
783
+ // 探针必须带路径令牌(被接管的实例是我们自己拉的,令牌在令牌文件里)——
784
+ // 没有令牌文件说明那个实例不是本插件当前这代拉起来的,不接管。
785
+ if (serve === 'loopback' && cfg.port !== 0) {
786
+ const existingToken = readToken(cfg);
787
+ const probe = await healthCheck(cfg.host, cfg.port, existingToken, 800);
726
788
  if (probe.ok) {
727
789
  const record = readPidFile(cfg);
728
790
  if (record && isAlive(record.pid)) {
729
791
  state.serve = 'loopback';
730
792
  state.status = 'running';
731
793
  state.pid = record.pid;
794
+ state.port = cfg.port;
795
+ state.token = existingToken;
732
796
  state.cwd = cwd ?? record.cwd ?? null;
733
797
  state.launchCwd = record.launchCwd ?? record.cwd ?? null;
734
798
  state.startedAt = record.startedAt ?? null;
@@ -736,7 +800,7 @@ export async function apply(ctx, config) {
736
800
  return snapshot();
737
801
  }
738
802
  state.status = 'error';
739
- state.error = `端口 ${cfg.port} 已被占用且没有有效的 pid.json 记录(拒绝误杀);请释放端口或修改 port 配置`;
803
+ state.error = `端口 ${cfg.port} 已被占用且没有有效的 pid.json 记录(拒绝误杀);请释放端口或把 port 改回 0(随机端口)`;
740
804
  return snapshot();
741
805
  }
742
806
  }
@@ -773,7 +837,12 @@ export async function apply(ctx, config) {
773
837
  args.push('--pipe', state.pipe);
774
838
  } else {
775
839
  state.pipe = null;
776
- args.push('--port', String(cfg.port), '--host', cfg.host);
840
+ // 随机端口(port=0)+ 路径令牌:令牌**不进程 argv**(本机任意进程都能读命令行),
841
+ // 走文件传递;实际端口由 launcher 写 endpoint 文件回报。
842
+ fs.rmSync(endpointFile(cfg), { force: true }); // 清掉上一实例的记录,避免读到旧端口
843
+ state.token = rotateToken(cfg);
844
+ args.push('--port', String(cfg.port), '--host', cfg.host,
845
+ '--token-file', tokenFile(cfg), '--endpoint-file', endpointFile(cfg));
777
846
  }
778
847
  if (typeof cfg.locale === 'string' && cfg.locale !== '') args.push('--locale', cfg.locale);
779
848
  }
@@ -783,6 +852,7 @@ export async function apply(ctx, config) {
783
852
  state.error = null;
784
853
  state.logTail = '';
785
854
  state.status = 'starting';
855
+ state.port = serve === 'dsh' ? null : cfg.port;
786
856
  state.cwd = cwd ?? null;
787
857
  state.launchCwd = cwd ?? null;
788
858
  state.startedAt = Date.now();
@@ -827,22 +897,44 @@ export async function apply(ctx, config) {
827
897
  }
828
898
  child = proc;
829
899
  state.pid = proc.pid ?? null;
830
- writePidFile(cfg, {
900
+ /** 实例记录:pid 一到手就先写(崩溃/回收要靠它);端口 0 时在拿到实际端口后再补写一次。 */
901
+ const writeInstanceRecord = () => writePidFile(cfg, {
831
902
  pid: proc.pid,
832
903
  startedAt: state.startedAt,
833
904
  cwd: cwd ?? null,
834
905
  launchCwd: cwd ?? null,
835
906
  host: serve === 'dsh' ? null : cfg.host,
836
- port: serve === 'dsh' ? null : cfg.port,
907
+ port: serve === 'dsh' ? null : state.port,
837
908
  serve,
838
909
  pipe: state.pipe,
839
910
  launchKind: launch.kind,
840
911
  launchCommand: launch.kind === 'bin' ? launch.command : launch.script,
912
+ tokenized: state.token !== null,
841
913
  });
914
+ writeInstanceRecord();
842
915
 
843
916
  proc.stdout?.on?.('data', appendLog);
844
917
  proc.stderr?.on?.('data', appendLog);
845
918
 
919
+ // 端口 0(默认)时 host 只能从 endpoint 文件知道实际端口:等 launcher 写完再开始就绪轮询。
920
+ if (launch.kind === 'launcher' && serve === 'loopback' && cfg.port === 0) {
921
+ const deadline = Date.now() + 10000;
922
+ let endpoint = null;
923
+ while (endpoint === null && Date.now() < deadline) {
924
+ endpoint = readEndpoint(cfg);
925
+ if (endpoint !== null && endpoint.pid !== (proc.pid ?? -1)) endpoint = null; // 旧进程的残留记录
926
+ if (endpoint === null) await new Promise((r) => setTimeout(r, 100));
927
+ }
928
+ if (endpoint === null) {
929
+ state.status = 'error';
930
+ state.error = 'launcher 未回报监听端口(endpoint 文件缺失);请查看启动日志';
931
+ return snapshot();
932
+ }
933
+ state.port = endpoint.port;
934
+ writeInstanceRecord();
935
+ console.log(`[code-server] 随机端口: ${state.port}(host=${endpoint.host}, pid=${endpoint.pid})`);
936
+ }
937
+
846
938
  proc.on('error', (err) => {
847
939
  if (child !== proc) return;
848
940
  child = null;
@@ -1094,19 +1186,27 @@ export async function apply(ctx, config) {
1094
1186
  console.log(`[code-server] 检测到旧版安装根 ${legacyRoot}(0.1.35 及更早遗留,已不再使用,可安全删除)`);
1095
1187
  }
1096
1188
 
1097
- // DSH host 重启后 adopt(仅 loopback 模式):pid.json 有效且进程存活且 /healthz 响应 → 接管。
1189
+ // DSH host 重启后 adopt(仅 loopback 模式):pid.json 有效、进程存活、令牌文件在、endpoint 记录的端口
1190
+ // 上 /healthz(带令牌)响应 → 接管。端口 0 时不能拿 cfg.port 去探(那是个占位值),必须用 endpoint 文件。
1098
1191
  // dsh 模式的 launcher 带 --parent-pid 看门狗:host 一退出它就自行退出,不存在可接管的孤儿。
1099
1192
  if (liveInstance && (record.serve !== 'dsh')) {
1100
- const probe = await healthCheck(cfg.host, cfg.port, 800);
1193
+ const adoptedToken = readToken(cfg);
1194
+ const endpoint = readEndpoint(cfg);
1195
+ const adoptPort = endpoint !== null && endpoint.pid === record.pid ? endpoint.port : (cfg.port !== 0 ? cfg.port : null);
1196
+ const probe = adoptPort === null || adoptedToken === null
1197
+ ? { ok: false }
1198
+ : await healthCheck(cfg.host, adoptPort, adoptedToken, 800);
1101
1199
  if (probe.ok) {
1102
1200
  state.serve = 'loopback';
1103
1201
  state.status = 'running';
1104
1202
  state.pid = record.pid;
1203
+ state.port = adoptPort;
1204
+ state.token = adoptedToken;
1105
1205
  state.cwd = record.cwd ?? null;
1106
1206
  state.launchCwd = record.launchCwd ?? record.cwd ?? null;
1107
1207
  state.startedAt = record.startedAt ?? null;
1108
1208
  state.adopted = true;
1109
- console.log(`[code-server] adopted running instance pid=${record.pid} port=${cfg.port}`);
1209
+ console.log(`[code-server] adopted running instance pid=${record.pid} port=${adoptPort}(令牌已启用)`);
1110
1210
  } else {
1111
1211
  removePidFile(cfg);
1112
1212
  }
@@ -1116,5 +1216,5 @@ export async function apply(ctx, config) {
1116
1216
 
1117
1217
  maybePrestart();
1118
1218
 
1119
- console.log(`[code-server] static plugin loaded (serve=${requestedServe()} host=${cfg.host} port=${cfg.port})`);
1219
+ console.log(`[code-server] static plugin loaded (serve=${requestedServe()} host=${cfg.host} port=${cfg.port === 0 ? '0=随机' : cfg.port})`);
1120
1220
  }
package/lib/launcher.mjs CHANGED
@@ -9,12 +9,25 @@
9
9
  * Module 解析、多处 process.exit),且 node-pty/sqlite 崩溃时必须只带走 IDE、不能带走 DSH host。
10
10
  *
11
11
  * 用法(由 lib/index.js 调用):node lib/launcher.mjs --tree <树根> --user-data-dir <dir>
12
- * --extensions-dir <dir> (--port <n> [--host 127.0.0.1] | --pipe <name>) [--parent-pid <pid>] [--locale zh-cn]
12
+ * --extensions-dir <dir> (--port <n> [--host 127.0.0.1] [--token-file <path>] [--endpoint-file <path>]
13
+ * | --pipe <name>) [--parent-pid <pid>] [--locale zh-cn]
13
14
  * 就绪信号:stdout 打印 `dshcs-ready <productPath> <mode> <addr>`。
15
+ *
16
+ * 安全模型(0.2.14 起):
17
+ * - **随机端口**:host 传 `--port 0`,由系统分配空闲端口,launcher 把实际地址写进 `--endpoint-file`
18
+ * (host 读回)。固定端口在 `port` 配置里显式指定时仍然照用。
19
+ * - **路径令牌**:`--token-file` 给出的随机令牌成为 URL 的**路径前缀**(`http://127.0.0.1:<port>/<token>/…`)。
20
+ * 为什么不走 VS Code 自带的 `connection-token`:它靠 `?tkn=` → 302 + `Set-Cookie: vscode-tkn; SameSite=Lax`,
21
+ * 而本插件的桌面端 iframe 是**跨源**的(dsh-app:// → 127.0.0.1),Lax cookie 在跨站子框架里不会被带上
22
+ * ⇒ 那样会把 desktop 直接打挂。路径前缀不需要 cookie:workbench 的资源与 WS 全部由 `location.pathname`
23
+ * 派生(`serve: dsh` 挂载在 /code-server/ 下已验证同一机制),前缀天然跟随每个子请求与 WS 握手。
24
+ * - **Host 白名单**:回环模式下只接受 `127.0.0.1|localhost|[::1]:<实际端口>`,挡 DNS rebinding
25
+ * (即便请求不带 Origin,originAllowed 也放行不了)。
26
+ * - 令牌只存在于文件与 URL 路径里,**不进 argv、不进日志**。
14
27
  */
15
28
 
16
29
  import { createServer as createHttpServer, request as httpRequest } from 'node:http';
17
- import { createReadStream, existsSync, mkdirSync, readFileSync, statSync } from 'node:fs';
30
+ import { createReadStream, existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from 'node:fs';
18
31
  import { connect } from 'node:net';
19
32
  import { dirname, extname, join, normalize, resolve, sep } from 'node:path';
20
33
  import { fileURLToPath, pathToFileURL } from 'node:url';
@@ -36,6 +49,10 @@ function parseArgs(argv) {
36
49
  parentPid: null,
37
50
  locale: null,
38
51
  disableProxy: false,
52
+ /** 路径令牌文件(host 写;回环模式下必给)。 */
53
+ tokenFile: null,
54
+ /** 实际监听地址回报文件(launcher 写;host 读)。 */
55
+ endpointFile: null,
39
56
  /** ?v= 缓存击穿标记(host 传 DSHCS_HTML_TAG;为空则每次启动生成一个)。 */
40
57
  htmlTag: process.env.DSHCS_HTML_TAG ?? null,
41
58
  };
@@ -47,6 +64,8 @@ function parseArgs(argv) {
47
64
  else if (a === '--host') out.host = argv[++i];
48
65
  else if (a === '--port') out.port = Number(argv[++i]);
49
66
  else if (a === '--pipe') out.pipe = argv[++i];
67
+ else if (a === '--token-file') out.tokenFile = argv[++i];
68
+ else if (a === '--endpoint-file') out.endpointFile = argv[++i];
50
69
  else if (a === '--parent-pid') out.parentPid = Number(argv[++i]);
51
70
  else if (a === '--locale') out.locale = argv[++i];
52
71
  else if (a === '--disable-proxy') out.disableProxy = true;
@@ -63,6 +82,20 @@ const tree = resolve(args.tree);
63
82
  const serverMain = join(tree, 'lib', 'vscode', 'out', 'server-main.js');
64
83
  if (!existsSync(serverMain)) fail(`不是一棵 VS Code 树(缺少 ${serverMain})`);
65
84
  if (args.pipe === null && !Number.isFinite(args.port)) fail('必须给 --port 或 --pipe');
85
+ // 端口 0 = 由系统分配空闲端口(host 从 --endpoint-file 读回实际值)
86
+ if (args.pipe === null && (!Number.isInteger(args.port) || args.port < 0 || args.port > 65535)) fail(`--port 非法: ${args.port}`);
87
+
88
+ /** 路径令牌:非空 → 要求 URL 以此为前缀(见文件头"安全模型")。 */
89
+ const TOKEN_RE = /^[0-9A-Za-z_-]{16,128}$/;
90
+ const pathToken = (() => {
91
+ if (args.tokenFile === null) return null;
92
+ let raw;
93
+ try { raw = readFileSync(args.tokenFile, 'utf8').trim(); } catch (error) { fail(`读不到令牌文件 ${args.tokenFile}: ${error.message}`); }
94
+ if (!TOKEN_RE.test(raw)) fail(`令牌文件内容不合法(应为 16~128 位 [0-9A-Za-z_-]): ${args.tokenFile}`);
95
+ return raw;
96
+ })();
97
+ if (args.pipe !== null && pathToken !== null) fail('管道模式下不需要 --token-file(DSH 侧已做鉴权)');
98
+ const prefix = pathToken === null ? null : `/${pathToken}`;
66
99
 
67
100
  const userDataDir = resolve(args.userDataDir ?? join(PACKAGE_ROOT, '.dshcs-data', 'user-data'));
68
101
  const extensionsDir = resolve(args.extensionsDir ?? join(userDataDir, '..', 'extensions'));
@@ -253,6 +286,31 @@ function originAllowed(req) {
253
286
  return host === origin;
254
287
  }
255
288
 
289
+ /** 回环模式下的 Host 白名单:只接受本机字面量 + **实际**监听端口。
290
+ * 为什么必须:originAllowed() 对"不带 Origin"的请求放行(本地工具/测试需要),而 DNS rebinding
291
+ * 恰恰能构造出不带 Origin 的请求 —— 没有这道检查,一个恶意页面就能用 127.0.0.1 以外的域名
292
+ * 打到本机端口上。端口在 listen 之后才确定,故用变量在请求到达时求值。 */
293
+ let boundPort = Number.isFinite(args.port) ? args.port : 0;
294
+ function hostAllowed(req) {
295
+ if (prefix === null) return true; // 管道模式:Host 由 DSH 的 webServer 决定,已过 requestRejection
296
+ const host = getHostHeader(req);
297
+ if (host === undefined || host === '') return false;
298
+ const m = /^(\[[0-9a-fA-F:.]+\]|[^:]+)(?::(\d+))?$/.exec(host);
299
+ if (m === null) return false;
300
+ const name = m[1].replace(/^\[/, '').replace(/\]$/, '').toLowerCase();
301
+ const port = m[2] === undefined ? 80 : Number(m[2]);
302
+ if (port !== boundPort) return false;
303
+ return name === '127.0.0.1' || name === 'localhost' || name === '::1' || name === String(args.host).toLowerCase();
304
+ }
305
+
306
+ /** 校验并剥掉路径令牌前缀。返回 null = 前缀不对(调用方回 404,不泄露"这里是个 IDE")。 */
307
+ function stripToken(url) {
308
+ if (prefix === null) return url;
309
+ if (url === prefix) return '/';
310
+ if (url.startsWith(`${prefix}/`)) return url.slice(prefix.length);
311
+ return null;
312
+ }
313
+
256
314
  // ---------------------------------------------------------------- HTTP / WS 分发
257
315
 
258
316
  /** 工作台 HTML 的响应改写:给 workbench.js / workbench.css / nls 资源加 `?v=<tag>` 缓存击穿。
@@ -310,16 +368,39 @@ function interceptHtmlResponse(res) {
310
368
 
311
369
  const server = createHttpServer((req, res) => {
312
370
  const url = req.url ?? '/';
371
+ // 令牌只出现在 URL 路径里 —— 别让它经 Referer 漏给站外资源
372
+ res.setHeader('Referrer-Policy', 'no-referrer');
373
+ if (!hostAllowed(req)) {
374
+ log(`拒绝 Host 头: host=${req.headers.host ?? '-'} ${req.method} ${url}`);
375
+ res.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' });
376
+ res.end('forbidden');
377
+ return;
378
+ }
379
+ // 前缀少了结尾斜杠 → 302 补上(否则文档里的相对引用会从根开始解析)
380
+ if (prefix !== null && url.split('?')[0] === prefix) {
381
+ const query = url.slice(prefix.length);
382
+ res.writeHead(302, { location: `${prefix}/${query.startsWith('?') ? query : ''}` });
383
+ res.end();
384
+ return;
385
+ }
386
+ const stripped = stripToken(url);
387
+ if (stripped === null) {
388
+ res.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' });
389
+ res.end('not found');
390
+ return;
391
+ }
392
+ req.url = stripped;
313
393
  // **按路径匹配,别拿整串比**:客户端加载文档时永远带查询串(`?folder=<cwd>`),
314
394
  // 用 `url === '/'` 判断会让改写整条失效(0.3.x 就踩过这个坑);`/healthz`、`/manifest.json`
315
395
  // 同理要用路径匹配,否则带查询串的探针会掉到 VS Code 那边变成 404。
316
- const urlPath = url.split('?')[0];
396
+ const urlPath = stripped.split('?')[0];
317
397
  if (urlPath === '/healthz') {
318
398
  res.writeHead(200, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' });
319
399
  res.end(JSON.stringify({
320
400
  ok: true,
321
401
  productPath,
322
402
  pid: process.pid,
403
+ port: boundPort,
323
404
  mode: args.pipe === null ? 'tcp' : 'pipe',
324
405
  htmlTag: args.htmlTag,
325
406
  recentUpgrades: recentUpgrades.slice(-3),
@@ -357,6 +438,17 @@ server.on('upgrade', (req, socket, head) => {
357
438
  const url = req.url ?? '/';
358
439
  recentUpgrades.push(url);
359
440
  if (recentUpgrades.length > 10) recentUpgrades.shift();
441
+ if (!hostAllowed(req)) {
442
+ log(`拒绝 Host 头的 WebSocket: host=${req.headers.host ?? '-'} url=${url}`);
443
+ socket.end('HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 0\r\n\r\n');
444
+ return;
445
+ }
446
+ const stripped = stripToken(url);
447
+ if (stripped === null) {
448
+ socket.end('HTTP/1.1 404 Not Found\r\nConnection: close\r\nContent-Length: 0\r\n\r\n');
449
+ return;
450
+ }
451
+ req.url = stripped;
360
452
  // 跨源防护(与 code-server 一致:只卡 upgrade,HTTP 侧 VS Code 仅接受 GET 且状态变更都走 WS)
361
453
  if (!originAllowed(req)) {
362
454
  log(`拒绝跨源 WebSocket:origin=${req.headers.origin} host=${req.headers.host ?? '-'} url=${url}`);
@@ -364,7 +456,7 @@ server.on('upgrade', (req, socket, head) => {
364
456
  return;
365
457
  }
366
458
  if (!args.disableProxy) {
367
- const target = proxyTarget(url);
459
+ const target = proxyTarget(stripped);
368
460
  if (target !== null) { handleProxyUpgrade(req, socket, head, target); return; }
369
461
  }
370
462
  socket.pause();
@@ -418,11 +510,35 @@ try {
418
510
  const listenTarget = args.pipe === null ? { host: args.host, port: args.port } : args.pipe;
419
511
  server.on('error', (error) => { fail(`监听失败(${JSON.stringify(listenTarget)}): ${error.message}`); });
420
512
 
513
+ /** 把**实际**监听地址写给 host(端口 0 时 host 只能从这里知道端口;原子写避免读到半截)。 */
514
+ function writeEndpoint(addr) {
515
+ if (args.endpointFile === null) return;
516
+ const payload = {
517
+ host: addr.address,
518
+ port: addr.port,
519
+ pid: process.pid,
520
+ productPath,
521
+ mode: args.pipe === null ? 'tcp' : 'pipe',
522
+ tokenized: prefix !== null,
523
+ startedAt: Date.now(),
524
+ };
525
+ try {
526
+ const tmp = `${args.endpointFile}.${process.pid}.tmp`;
527
+ writeFileSync(tmp, JSON.stringify(payload, null, 2), 'utf8');
528
+ renameSync(tmp, args.endpointFile);
529
+ } catch (error) {
530
+ log(`写 endpoint 文件失败(${args.endpointFile}): ${error.message}`);
531
+ }
532
+ }
533
+
421
534
  server.listen(listenTarget, () => {
422
535
  const addr = server.address();
423
536
  const shown = typeof addr === 'string' ? addr : `${addr.address}:${addr.port}`;
537
+ if (typeof addr === 'object' && addr !== null) boundPort = addr.port;
538
+ if (typeof addr === 'object' && addr !== null) writeEndpoint(addr);
424
539
  log(`listening on ${shown} (tree=${tree})`);
425
540
  log(`user-data-dir=${userDataDir} extensions-dir=${extensionsDir}`);
541
+ log(`path token: ${prefix === null ? '(无)' : '已启用(URL 前缀,不写日志)'}`);
426
542
  console.log(`dshcs-ready ${productPath} ${args.pipe === null ? 'tcp' : 'pipe'} ${shown}`);
427
543
  });
428
544
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-code-server-app",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
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). 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-11T14:13:32.827Z",
6
+ "preparedAt": "2026-09-11T14:42:28.333Z",
7
7
  "source": "registry",
8
8
  "node": "v24.13.1",
9
9
  "platform": "win32",