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 +37 -4
- package/README.md +34 -8
- package/lib/client.js +1 -1
- package/lib/index.js +118 -18
- package/lib/launcher.mjs +120 -4
- package/package.json +1 -1
- package/vendor/VENDOR.json +1 -1
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,
|
|
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 (`
|
|
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` | `
|
|
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`
|
|
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
|
|
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`(默认)** | 插件自己起一个回环端口(`
|
|
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` | `
|
|
445
|
-
| `auth` | `none` | 固定 `none`(0.2.0 起 argon2
|
|
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`
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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(仅
|
|
724
|
-
|
|
725
|
-
|
|
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 记录(拒绝误杀)
|
|
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
|
-
|
|
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
|
-
|
|
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 :
|
|
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
|
|
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
|
|
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=${
|
|
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]
|
|
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 =
|
|
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(
|
|
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.
|
|
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": {
|
package/vendor/VENDOR.json
CHANGED
|
@@ -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:
|
|
6
|
+
"preparedAt": "2026-09-11T14:42:28.333Z",
|
|
7
7
|
"source": "registry",
|
|
8
8
|
"node": "v24.13.1",
|
|
9
9
|
"platform": "win32",
|