jinsiyu/dsh-code-server-app
静态 profile 插件(npm 包形态,host + client bundle),把 code-server 发行版里的 VS Code server 树 作为平台无关依赖随插件安装(打包期产物 vendor/vscode → @jinsiyu/dshcs-vscode-server, 无安装脚本、无 postinstall);code-server 的 Node 服务层已由插件自带的 lib/launcher.mjs 取代 (它直接驱动 /lib/vscode/out/server-main.js 的 loadCodeWithNls() / createServer() / handleRequest() /
catalog descriptioncatalog 简介 / catalog description:将code-server(VSCode网页版)打包安装到dsh内的插件,快速实现专业的文件编辑。Package and install code-server (the web version of VSCode) as a plugin within dsh to quickly achieve professional file editing.
Project Overview项目介绍
DSH static plugin that bundles code-server 4.134 as a dependency, embedding it with no global install required. A draggable floating ball opens an iframe window that follows the active workspace, with green/yellow/red status indicator, maximize, 8-direction resize, and Esc close. Use it to bring a full VS Code editor into DSH. Caveat: extensions come from Open VSX, so proprietary Microsoft extensions like GitHub Copilot need manual .vsix install; Windows folder paths require forward slashes like /C:/...; reverse-proxy subpath is unsupported.
DSH 静态插件,把 code-server 4.134 作为依赖随装、零全局配置,浮窗 iframe 直连并跟随活动工作区自动重启;悬浮球可拖动记忆位置,状态点显示绿/黄/红,支持最大化、8 向缩放、Esc 关闭。注意:扩展市场为 Open VSX,GitHub Copilot 等微软专有扩展需手动下载 .vsix 安装;Windows 路径需正斜杠形如 /C:/...;子路径反向代理不支持,必须独立端口。
请帮我了解并安装插件:【dsh-code-server-app】【https://github.com/jinsiyu/dsh-code-server-app】
Send this message to DSH in your current session. CLI install commands may not be accurate across systems — DSH will figure it out for you.把上面这条消息直接发给当前会话里的 DSH,让它帮你了解并安装。安装命令不一定准确,发给 DSH 更稳。
Or use CLI install (for developers)或使用命令行安装(适合开发者)
CLI Install命令行安装
dsh plugin --profile web add dsh-code-server-app@0.2.1
把 jinsiyu/dsh-code-server-app 加入你的 DSH 配置(web profile)即可启用。
READMEREADME
dsh-code-server-app — 在 DSH 中集成 code-server(VS Code 网页版)
源码仓库地址见
package.json的repository/homepage字段。
⚠️ 扩展市场说明(重要)
- code-server 的扩展商店是 Open VSX,不是微软 Visual Studio Marketplace;
- 微软 Marketplace 的条款禁止第三方产品(含 code-server)使用其 API,所以 code-server 无法查询微软市场的扩展列表;
- 因此微软商业/专有扩展(如 GitHub Copilot、Remote-SSH 等 Remote 系列、Azure 系列、IntelliCode)在商店里找不到——这是微软发行策略,不是缺失;
- 微软开源系扩展(Python、TypeScript 调试、ESLint 等)在 Open VSX 有镜像,搜索正常可装;
- 需要微软专有扩展时:从 Marketplace 网页下载
.vsix,用code-server --install-extension <文件>(或放入--extensions-dir)手动安装,即可在插件列表使用。
静态 profile 插件(npm 包形态,host + client bundle),把 code-server
发行版里的 VS Code server 树 作为平台无关依赖随插件安装(打包期产物 vendor/vscode → @jinsiyu/dshcs-vscode-server,
无安装脚本、无 postinstall);code-server 的 Node 服务层已由插件自带的 lib/launcher.mjs 取代
(它直接驱动 <树>/lib/vscode/out/server-main.js 的 loadCodeWithNls() / createServer() / handleRequest() /
handleUpgrade(),并补上 /healthz、/manifest.json、/_static/*、/proxy/:port 这几条 code-server 原本提供的 HTTP 面);
原生模块(node-pty / @vscode/sqlite3 / spdlog …)由 @jinsiyu/dshcs-* 子包按真名直接挂在插件依赖上、按 os/cpu 自动选中 ——
无需全局 npm 安装、无需配置 bin、无需改 profile 配置、无需第二条安装命令、无需 argon2/C++ 工具链。
0.2.0 起:argon2 与 code-server 的 136 个运行时依赖(express / proxy-agent / js-yaml / pem / limiter …) 全部不再随包分发(减少 ~34.5MB + 一条原生构建链);IDE 提供方式见下方「服务方式(serve)」。 依据与实测证据见
docs/analysis-code-server-as-dsh-plugin.md(含子路径挂载、WS 路径、命名管道、fence 的逐项验证)。0.3.22 起:「问 DSH」面板直接渲染 DSH 官方的 markdown 结果(与 DSH 界面同一份渲染器 + 同一套设计令牌, 只渲染该会话的新内容),并且就在面板里处理授权(工作区外写入 / 执行命令)—— 详见 「与 DSH 的协同:编辑器桥」与
docs/analysis-code-server-as-dsh-plugin.md第 21 节。
UI 载体与 DSH 版本要求(0.2.3 起只支持带右侧栏的 DSH)
| DSH 版本 | 载体 | 入口 |
|---|---|---|
≥ 0.1.5-alpha.1(有 sidebarRight / sidebarRightTabs 服务) |
右侧栏标签(kind=code-server,标签名 Code Server),并认领文件地址(见下) |
① DSH 官方的产物 chip / 「交付」卡片预览 / 正文里的文件名(0.2.5 起,走官方 openFile → 文件地址 → 本 tab);② 右侧栏「开始」页的 Code Server 入口框;③ 设置 → 插件 → Code Server → 「在右侧栏打开」 |
| 更早(无右侧栏服务) | 不受支持:除设置页的一条提示外不提供任何入口 | 无(设置 → 插件 → Code Server 显示升级提示) |
- 检测方式:先
ctx.get('sidebarRightTabs') / ctx.get('sidebarRight')同步探测; 服务可能晚于本插件就绪,则ctx.inject(['sidebarRightTabs','sidebarRight'], …)等待, 2.5 s 内仍未就绪即判定旧版 DSH(不按版本号硬判,也不影响插件激活)。 0.2.4 起判定可逆、且注册不再依赖ctx属性访问(desktop 上曾因此静默不注册、表现为"设置卡正常但侧栏没有入口"):- 服务查找先读
ctx.<name>,再回退ctx.get(name)——两种上下文形态都能注册; - 同步已能看到服务、但
inject迟迟不回调时,1.5 s 后用同步服务兜底注册; - 2.5 s 只给设置页提示,10 s 仍无服务才通知 host 回收/停止预启动(避免误杀慢启动的宿主);
- 服务晚到 → 自动撤销旧版判定、补注册侧栏,并上报
{sidebar:true}让 host 恢复; - 注册失败不再静默:控制台报错,设置卡入口行显示"已探测到右侧栏服务,但标签注册失败"。
- 服务查找先读
- 0.2.3 起不再兼容旧版 DSH:悬浮球与内部浮动窗口回退已删除。判定为旧版时:
- 只注册设置卡片,内容是一条升级提示(见下),不注册悬浮球/浮窗/文件地址认领,也不预热 IDE;
- 客户端向 host 上报
/api/code-server/ui-mode { sidebar:false }(在上面的 10 s 宽限之后),host 据此回收自动预启动的实例 并停止预启动(用户手动启动的实例不受影响);服务随后才出现时会再上报{sidebar:true}撤销; - 升级 DSH 后无需重装插件,刷新页面即可,本页会恢复为完整设置卡片。
- 侧栏标签内即 code-server 页面(iframe),跟随当前会话工作区;面板可折叠/分屏/浮动/全屏(由 DSH 右侧栏提供)。
- 打开即全屏(0.2.9 起,默认开):打开 Code Server 标签(含点开产物 chip / 交付卡片 / 正文文件名)时,
自动把右侧栏从"与对话并排"切到全屏(铺满窗口)——IDE 在窄栏里太挤。
只影响"打开那一刻":随时点右侧栏的「退出全屏」不会被抢回去;切走再切回、再次打开文件 tab 会重新切全屏。
不想要就在设置卡片里关掉(
fullscreenOnOpen=false)。- 实现说明:DSH 没有把"模式"开放给插件 ——
ctx.sidebarRight只有isExpanded/toggleExpanded(展开/收起), push ⟷ fullscreen 记在ui-sidebar-right自己的 store 里(actions.setMode,只发给它的 seat 内部组件);ctx.layout.openRightbar(track, fullscreen)也不是控制面,而是 seat 用来汇报 presentation 的通道 (上游源码注释:the occupant reports it; nothing else writes it)。 所以本插件做的是"用户那个动作本身":closest('[data-sidebar-right-panel]')定位自己所在面板, 再点面板 chrome 上的[data-sidebar-right-mode="fullscreen"]按钮(与手点完全同一条路径, 含窄视口下的连带处理)。按钮找不到时保持原模式并console.warn一条,绝不影响面板渲染。
- 实现说明:DSH 没有把"模式"开放给插件 ——
- IDE 常驻(0.2.2 起,默认开):切到别的标签/收起侧栏再回来不再重载 code-server—— 未保存的编辑缓冲区、终端、调试会话都留在原处(见下方「为什么切标签不再重载」)。
- 设置卡片只有三个设置:「认领类型」「打开即全屏」与「后台常驻(切标签不重载)」——没有其它行(0.2.7 起移除「入口」「依赖安装」「环境检测」)。
打开 IDE 用右侧栏「开始」页的 Code Server 入口框,或直接点官方的产物 chip / 交付卡片 / 正文文件名;
诊断看 DSH host 日志里的
[code-server]输出(/api/code-server/status仍返回env供脚本排查)。 旧版的windowedOpen(窗口化打开)、reserveComposer已在 0.2.6 移除:旧设置文档里残留的这两个键不会报错,只是被忽略(不再出现在 schema 里)。 需要在新标签页用 IDE 时,从设置卡/空态提示里复制完整地址(含路径令牌,http://127.0.0.1:<port>/<token>/;serve: dsh时为 DSH 的/code-server/)—— 少了令牌那一段会 404。
文件打开(0.2.5 起走官方入口)
DSH 用资源地址命名文件,openFile 只负责把地址交给右侧栏去认领:
官方产物 chip / 「交付」卡片预览 / 正文内联提及
→ openFile(path, { line? }) (ui-chat 提供)
→ dsh-resource://file/session/<sessionId>/<path> (或 …/file/absolute/<path>)
→ ctx.sidebarRight.openResource(address)
→ 由注册了匹配 patterns 的 tab 类型认领(优先级 extension(3) > builtin(2) > fallback(1),
同带内按 pattern 长度、再按注册顺序)
本插件注册时带上:
| 字段 | 值 | 作用 |
|---|---|---|
patterns |
['dsh-resource://file/**'] |
认领文件地址(含 : 的 pattern 按整址 glob 匹配) |
priority |
'extension' |
高于官方纯文本预览的 fallback——DSH 源码注释写明后者是"VS Code 文本编辑器在编辑器中的位次,任何更具体的类型都应当击败它" |
canOpen |
见下 | 按「认领类型」设置否决,未认领的地址由官方预览兜底 |
title |
地址末段(=文件名) | tab chip 显示文件名;页面 tab(sidebar://code-server)仍是 Code Server |
- 认领类型(设置卡片里的文本框,
claimExtensions,0.2.11 起): 不再区分作用域 ——dsh-resource://file/session/…与…/file/absolute/…一视同仁,只看扩展名。 文本框语法(分号分隔,,/空白/换行也认;写py、.py、*.py等价;大小写不敏感):*= 其余类型也认领(兜底);py= 认领.py;!md= 不认领.md(排除优先于认领与*);- 默认
*;!md;!markdown;!html;!htm;!png;!jpg;!jpeg;!gif;!webp;!bmp;!ico;!svg;!pdf= DSH 自带预览渲染得好的四类(markdown / html / 图片 / PDF)留给它,其余(代码、json/yaml、txt、日志、 无扩展名如Makefile、未知扩展名)都进 IDE;清空文本框 = 不认领任何文件(只保留页面 tab)。 - 三种实际形态:纯白名单(
py;ts,无*→ 其余不认领)、兜底(*)、兜底加排除(默认)。 - 语法、默认值与解析都在
lib/claim-types.js(host 的Config默认值与客户端canOpen共用同一份, 随包发布,不会两边漂移);单测scripts/test-claim-types.mjs。
- tab body 怎么定位文件:从
useTabInfo().tab.navigation.address解析出会话与路径 (src/address.js,与 DSHparseFileAddress同语义),相对路径按该会话 cwd 展开成绝对路径, 再把绝对路径 + 可选line交给 host 的/api/code-server/open-file;内建扩展 (dshcs-open-file)在 workbench 里showTextDocument(带行号时定位到该行)。 - 一个地址 = 一个 tab(官方语义,
contentId就是地址):打开三个文件会有三个 chip, 但它们共用同一个常驻 workbench(我们的 IDE 是单实例),切换时只是让 workbench 定位到对应文件。 - 为什么还留着那个内建扩展:VS Code Web 没有"从外部打开文件"的官方 API(唯一入口是
?folder=指定工作区),所以"让 workbench 定位到某个文件"只能由树内的扩展完成; host 写信号文件、扩展轮询并showTextDocument,失败保留重试(实例尚未就绪时也不会丢)。
为什么切标签不再重载(IDE 常驻)
过去的坑:DSH 的右侧栏(ui-dockkit)TabPanel 只渲染当前激活标签的 body
(TabPanel.tsx:412 → renderTab(active))——切到别的标签 = React 卸载该 body = iframe 被移出文档 =
浏览上下文销毁,切回来就是一次完整的 VS Code 重载(未保存的缓冲区丢失)。把标签浮动成独立面板只是绕开它,
并没有解决。
现在的做法(客户端 src/surface.js,0.2.2):插件把 iframe 从 React 手里接管,做成单例常驻面:
| 场景 | 动作 | 结果 |
|---|---|---|
| 标签激活 | host.moveBefore(frame, null) 移进当前可见的停靠位 |
状态保持型原子移动,不重载 |
| 标签失活 / 收起侧栏 | 移回文档级 park 容器(离屏、保留最后停靠尺寸、inert + aria-hidden) |
面不销毁,后台继续跑 |
| 工作区 / 端口变化 | 显式设置 src |
这是唯一正常的"重载"入口 |
- 为什么是
moveBefore:浏览器实测(Edge/Chromium 151)普通appendChild移动 iframe 会让内部计时器归零 (等价重载),而Element.moveBefore()(Chromium ≥133)保持状态(计时器 1→2 连续)。 - 降级不静默:
moveBefore缺失、或宿主已被 React 摘除而抛HierarchyRequestError: invalid hierarchy(passive effect cleanup 晚于 DOM 卸载)时,退回appendChild——会重载一次,但绝不丢帧; 状态里degraded/lastMoveError明示,界面据此提示"常驻不可用"。 - 重绘兜底(实测坑):真实 GUI 里观测到一次"元素在、画面不重绘"——iframe 尺寸、命中测试、
visibility全部正常,面板却一片白(连续两张截图哈希相同,确认没有新帧);translateZ(0)、opacity微调无效,display:none → 强制重排 → 还原(同一个 JS 任务内)可恢复,且 iframe 文档不重载、内部状态不变、无可见闪烁。 触发条件未能复现:探针页里moveBefore停放 337 s(超过 Chrome 对不可见跨源 iframe 的 ~5 min 节流窗口) 后移回、且关掉修复,仍正常绘制。因此把它当兜底保留:每次「停放 → 停靠」补一次nudgeRepaint()(surfaceSnapshot().nudgeCount计数,setNudgeEnabled(false)可现场 A/B)。 - 后台预热:配置
keepResident(默认true)时,宿主在插件启动后就把面建好并停在停放区, 首次点开标签无需冷启动等待;preload不会把正在使用的面拽走。 - 排障句柄:控制台可用
window.__dshcsSurface(snapshot()/setParkStrategy('offscreen'|'behind')/dock()/park()/nudge()/setNudgeEnabled(false)/destroy())。
实测记录(DSH web GUI,sidebar 标签间真实鼠标切换):切走 → docked:false、iframe 仍为同一节点、内部探针存活、
degraded:false;切回 → docked:true、src 不变、IDE 画面与编辑状态保持(无整页重载)。
完整证据与探针脚本见 docs/analysis-code-server-as-dsh-plugin.md。
服务方式(serve)
| 方式 | 说明 | 需要 |
|---|---|---|
loopback(默认) |
插件自己起一个回环端口(默认 port: 0 = 每次启动由系统分配随机端口),右侧栏 iframe 跨源直连;URL 带随机路径令牌(http://127.0.0.1:<port>/<token>/,见下「回环端口的安全模型」);进程可被 adopt(DSH host 重启后接管) |
无 |
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 |
回环端口的安全模型(0.2.14 起)
loopback 是 desktop 端唯一的通路(无 webServer、无同源挂载),所以它单独加固了一层:
- 随机端口:
port默认0→ 由系统分配空闲端口,launcher 把实际端口写进$DSH_HOME/code-server/endpoint.json, host 读回(因此端口每次都变、也不存在"8090 被占用"这类冲突)。要固定地址就显式配port。 - 路径令牌:每次新启动生成 32 位随机令牌(
[0-9A-Za-z_-],24 字节随机),写在$DSH_HOME/code-server/path-token(用户 profile 下,默认 ACL 仅本人可读),成为 URL 的路径前缀。 没有这个前缀的请求一律 404(不泄露"这里跑着 IDE"),前缀不带结尾斜杠会 302 补上。 - 为什么不用 VS Code 自带的
connection-token:它靠?tkn=→ 302 +Set-Cookie: vscode-tkn; SameSite=Lax; 而 desktop 的 iframe 是跨源的(dsh-app://→127.0.0.1),Lax cookie 在跨站子框架里不会被带上 → 会让 IDE 直接打不开。路径前缀不需要 cookie:workbench 的资源与 WS 全部由location.pathname派生 (serve: dsh挂在/code-server/下已验证同一机制),前缀天然跟随每个子请求与 WS 握手。 (已用真实 Edge + CDP 在跨源 iframe 里验证:随机端口 + 令牌下 workbench 正常渲染并建立 WS。) - Host 白名单:回环模式只接受
127.0.0.1 | localhost | [::1] : <实际端口>。挡的是 DNS rebinding —— 这类攻击构造的请求可以不带Origin,只靠Origin == Host那条检查拦不住。 Referrer-Policy: no-referrer:令牌在路径里,不能让它在加载站外资源时经Referer漏出去。- 令牌不落 argv、不进日志:命令行对本机任意进程可见,所以走文件传递;日志里只打印"已启用"。
边界(说清楚,不夸大):这一层挡的是"本机其它应用/端口扫描器/浏览器页面"顺手访问你的 IDE; 同用户的本地恶意程序本来就能直接读你的文件、也能读那个令牌文件 —— 那不在本插件的威胁模型内。
在
cordis.patch.yml的config.serve(或设置文档里的code-server.serve)切换,下次启动生效 —— 设置卡片不提供这一行(卡片只有认领类型/打开即全屏/后台常驻三个设置)。dsh模式的实际收益:单一 URL/单一端口(远程访问 DSH 即可用 IDE)、不再暴露额外回环端口、认证与 DSH 同级。dsh模式的两点已知取舍:- iframe 与 DSH 同源 → 该模式下不再挂
sandbox(同源 +allow-same-origin可被 frame 自行摘除,属"看起来有防护");loopback模式跨源,sandbox保持原样作为真防护。剪贴板仍由allow="clipboard-read; clipboard-write"提供。 - 转发端口(Ports 面板)的 WebSocket 无法用精确升级路由覆盖(端口号在路径里)→ 该功能在
dsh模式下不可用; HTTP 转发端口正常;需要端口转发 WS 时请用loopback模式。
- iframe 与 DSH 同源 → 该模式下不再挂
loopback模式下 upgrade 会做 code-server 同款 Origin 校验(0.2.1 起):带Origin时其 host 必须等于Host(含Forwarded: host=/X-Forwarded-Host的反代语义),否则回403;缺Origin的非浏览器请求放行。 没有这道检查时,本机任意浏览器页面都能对ws://127.0.0.1:<port>/stable-<commit>完成握手并驱动 IDE。
与 DSH 的协同:编辑器桥(0.3.0 起,默认开)
"IDE 就在旁边"和"agent 真的知道编辑器里发生了什么"是两件事。编辑器桥补的是后一半:只读地把 只有编辑器才知道的信息交给 agent,并让用户在编辑器里的动作能反过来驱动当前会话。
双向能力
| 方向 | 能力 | 落地方式 |
|---|---|---|
| 编辑器 → agent | 未保存缓冲区(磁盘内容 ≠ 用户所见)、活动文件与选区、语言服务器诊断(含 file:line、来源、code) | agent 工具 editor_context / editor_diagnostics;写脏文件前额外附一条提醒 |
| 编辑器 → DSH | 选中代码 → 右键「DSH: 针对选中内容提问」→ 打开提问面板(带 文件:行 与选中内容);提问以用户输入进当前会话,该会话的新内容用 DSH 官方 markdown 渲染器显示在面板里 |
命令 dsh-code-server.askAboutSelection(编辑器右键菜单最上面两条之一)+ webview 面板 + POST /ask + /sync 的 thread 字段 |
| DSH → 编辑器(授权) | agent 要写工作区外的文件 / 执行命令时的授权请求 → 面板里就地弹卡片(工具名 + 原因 + 倒计时),点「允许一次 / 拒绝」立刻生效 | /sync 的 approvals 字段 + POST /approve(桥里唯一的非只读路由,约束见「安全模型」) |
| agent → 编辑器 | agent 改了哪个文件 → 开原生 diff 审阅;缓冲区有未保存改动时告警而不覆盖 | host 观察 tools/result,扩展轮询后开 diff + 非模态告警 |
- 工具只在桥就绪时注册(IDE 没起来时模型看不到"有个用不了的工具");提示词段落也只在桥存活时渲染。
- 提问面板(扩展 0.2.0 起;0.2.3 起正文走官方渲染器;0.2.5 起是浮在 DSH 界面上的对话框): 右键命令不再开编辑器里的面板(那是编辑器的一个 tab/一列,怎么开都不像对话框),而是让 DSH 页面里的 插件客户端弹出一个可拖动、可缩放的浮动对话窗(右下角,✕ 关闭),不占编辑器版面; 面板开着的同时还能改选区再问。老宿主(探测不到对话框能力)才退回编辑器里的 webview 面板。 两个命令的意图分开记(0.3.21):「针对选中内容提问」只有真的选了内容才带行区间 + 选区正文; 「针对当前文件提问」永远不带行号、不带选区 —— 光标停在哪一行跟问题无关,行号只会误导 agent; 没选区时用选中命令提问也会退化成纯文件。
- 注入的上下文是折叠的(0.3.24):桥拼进消息的位置行 + 选区代码块会被拆出来,显示成一行默认收起的 「上下文」(点开才看得到那段代码),气泡里只留你的原话 —— 与 DSH 界面处理注入上下文的方式一致。
- 面板里的正文就是 DSH 的渲染结果(0.3.22):面板打包了 DSH 官方的 markdown 渲染器
(
@deepseek-ai/dsh-client-ui-primitives的MarkdownText)与官方设计令牌 —— 同一套 micromark/mdast 管线、 同一个增量流式解析器、同一个 shiki 高亮(启动集 typescript / shellscript / json)、KaTeX 公式、同样的标题与表格排版。 只渲染新内容(从面板订阅那一刻起),不重放历史、没有"加载更早"。 - 思考过程也照官方显示(0.3.23):助手的 reasoning 以「思考」行出现 —— 默认收起、收起时显示首行
(流式时显示最新一行)、点整行展开全文,用的就是官方
DisclosureRow+ 官方的思考图标与排版语言。 - 授权就在面板里处理(0.3.22;0.3.23 修好窗口):面板打开着的时候,该会话的授权请求先问面板(默认 5 分钟),
点「允许一次」/「拒绝」立刻生效;关掉面板或等满窗口就把请求原样交回官方链路(DSH 界面照旧弹卡)。
0.3.22 的 8 秒窗口对人来说太短 —— 卡片还没读完按钮就灰了(实测反馈「授权框失效了」),现在窗口是 5 分钟,
而且面板一关就立刻交回、不干等。
永不自动放行 ——
allowed-once只能来自你的一次点击,面板里没有"以后都允许"这种入口。 - 提问进 DSH 会话时是普通用户消息(
source: { kind: 'user' },host 0.3.19 修正):早期版本用{kind:'plugin'}会被 DSH 渲染成"上下文更新",看起来不像自己说的话;来源信息靠正文首行From the editor: <file>[:<行>]保留。 - 只读 + 一个受限例外:桥不写文件、不改文档、不执行命令;唯一的非只读路由是
POST /approve, 它只能回答已经存在的授权请求(见「安全模型」第 2 条)。agent 的写操作仍然全部走它自己的fs工具, 桥只是"知道它写了什么"、并把你对授权的答复带回去。 - 编辑器侧的入口还有状态栏的
$(plug) DSH(连通时显示,点击打开日志),日志在输出面板 「DSH Editor Bridge」里 —— 出问题时先看它。 - 扩展装在内置目录(0.3.12 修正):
dshcs-editor-bridge与dshcs-open-file一样装进<树>/lib/vscode/extensions/。0.3.0–0.3.11 装的是用户级目录,而 VS Code 服务端会把 "在用户扩展目录里、不在任何 profile 清单里"的扩展标进.obsolete(日志Marked extension as removed) 并永远跳过它 —— 每一轮启动都再标一次,桥因此从来没有上报过状态。 要关掉桥请用插件设置editorBridge=false(不挂桥、不注册工具),不要再指望在扩展视图里卸载它。
三条通道(0.3.13 起走本机 IPC:Windows 命名管道 / unix socket)
扩展 → host POST /code-server-bridge/sync 一趟来回:上报编辑器状态(+ 面板在看哪个会话)+ 取回待处理事件与对话流
扩展 → host POST /code-server-bridge/ask 把编辑器里的提问投进当前会话
扩展 → host POST /code-server-bridge/approve 回答一条**已经存在**的授权请求(唯一的非只读路由)
扩展 → host GET /code-server-bridge/health 无鉴权探活(便于重启后一眼确认)
扩展 → host POST /code-server-bridge/event 扩展上报打开/关闭文件等(进 host 日志尾)
host → 扩展 <extensionsDir>/.dshcs-bridge/bridge.json 端点 + 令牌(扩展每 5s 重读)
(同一份内容还会写到**内置扩展旁边** `<树>/lib/vscode/extensions/.dshcs-bridge/` ——
环境变量只在 host spawn IDE 时注入,而被**接管**的 IDE 是上一次启动的进程、拿不到它,
扩展得能只靠自身位置读到配置)
请求走 http.request({ socketPath })(fetch 不支持 socket),不开任何端口。
/sync 的响应里,面板真正用到的三段(0.3.22):
| 字段 | 内容 | 面板怎么用 |
|---|---|---|
thread |
被面板 watch 的会话的新内容条目(user / assistant / tool / approval),有界:每会话 ≤120 条、单条正文 ≤8000 字符、同时 watch ≤4 个会话 |
助手正文交给官方渲染器;工具与授权是紧凑摘要行 |
approvals |
待决授权请求 [{id, toolName, reason, at}](≤4 条) |
弹卡片 + 倒计时;点按后 POST /approve |
approvalHoldMs / uiVersion |
授权窗口长度(默认 300000ms = 5 分钟)/ DSH 界面版本 | 倒计时基准;渲染器版本不一致时提示 |
为什么不是 HTTP(0.3.13 定论,三条都实测过)
- desktop 根本没有 HTTP 面:渲染进程经 Electron IPC 调
host.fetch()(apps/desktop-host/src/index.ts:308的createSharedFetchHandler('/api'))—— 那是进程内函数调用, 进程外不可达;插件能挂 HTTP 的只有 web profile 的webServer。/api也不行:Connection 给/api装了 Host/Origin/cookie fence (packages/client/connection/src/index.ts里requestRejection→ 无 cookie 即 401),而桥的客户端 是扩展宿主里的 Node 进程 —— 它永远拿不到浏览器 cookie。实测(0.3.7):扩展按/api/code-server/bridge/sync轮询,要么 405(打到 launcher/VS Code)、要么 401(打到 /api fence), 桥从来没有真正同步过。- 桥的两端本来就是同一台机器上的两个进程(扩展宿主 ← 插件 spawn 的 IDE ← 插件)。 本机 IPC 比开端口更小:没有网络面、没有 Host/Origin 混淆代理问题,web 与 desktop 走同一条路。 令牌校验照旧保留(见下),Windows 管道名带随机后缀、POSIX socket 文件
chmod 0600。历史:0.3.9–0.3.12 挂在 DSH 的
webServer前缀下 —— 于是 desktop 永远休眠(没有 webServer)。
为什么状态是"推"而不是"拉":扩展宿主是 VS Code server 的一个子进程,不监听任何端口 —— host 反向请求不到它。所以编辑器状态只能在扩展主动发起的那趟轮询里带上来,host 缓存后给工具读 (缓存滞后最多一个轮询周期 600ms,超过 10s 没更新就判为过期,工具会明说"状态已过期");
为什么不用 SSE/WebSocket:扩展宿主里没有 HTTP 服务器,而桥的形态是"每 600ms 一趟请求/响应"。 轮询给了两条好性质:幂等(丢一次事件只是少一次提示,数据本身永远在编辑器里),以及状态天然最新(每趟都刷新)。
安全模型(五条不变量,改 lib/bridge.mjs 之前先读)
桥的令牌写在 <extensionsDir>/.dshcs-bridge/bridge.json(对本机同用户进程可读),所以:
/code-server-bridge/*只读,只有/approve一个例外。 没有写文件、改文档、执行命令、 拉起进程的路由。令牌泄露的爆炸半径被封在"看到编辑器里的信息",不会变成任意文件写/任意命令执行。scripts/test-bridge-routes.mjs里有一条白名单断言盯着这件事(未知后缀一律 404)。/approve的四条约束(缺一条就等于开了任意命令执行的后门,不许放宽): (a) 只能回答已经存在的授权请求,请求体只有{id, outcome},不接受任何自由文本 / 路径 / 命令参数 —— 它只能"回答问题",不能"发起动作";(b)id必须是本进程自己发起、且仍未决的请求(用后即废); (c)outcome只接受allowed-once/rejected,没有"永久允许"; (d) 没有面板在看 / 面板关掉 / 窗口超时(默认 5 分钟)→ 交回官方链路,绝不自动放行 (DSH 的approval/request本身 fail closed,这里只能把"没人答"保持成"没人答")。pnpm test:webview-bundle里有针对这四条与宿主侧白名单的断言。- 带
Origin的请求一律 403。 浏览器发起必带 Origin(含沙箱 iframe 的Origin: null), 扩展宿主是 Node 进程、不带。判定顺序上 Origin 先于令牌 —— 否则等于给浏览器一个 "令牌猜对没有"的 oracle。 实现细节:Node 路由 → Fetch 适配器把原始 headers 挂在 request 上(dshcsRawHeaders), 因为 undici 的Request构造器会把origin当 forbidden header 归一化掉 —— 读request.headers会让这道 403 静默失效(测试里有这条实测记录)。 - 路径收敛在编辑器当前工作区(
workspaceFolder之外的诊断直接丢弃)。 - 有界:诊断默认 200 条 / 单条截断 500 字符 / 上报体上限 256KB / 事件环形缓冲 64 条 / 对话流每会话 ≤120 条(单条正文 ≤8000 字符、同时 watch ≤4 个会话)/ 待决授权 ≤4 条。
这一层挡的是"本机其它应用或浏览器页面拿到那个文件后乱调桥";同用户的本地恶意程序 本来就能直接读你的文件与令牌文件 —— 那不在本插件的威胁模型内(与「回环端口的安全模型」同一句话)。
开关与诊断
| 怎么关 | 效果 |
|---|---|
cordis.patch.yml 的 config.editorBridge: false |
下次启动不写 bridge.json、不注册工具 |
设置文档里的 code-server.editorBridge: false |
即时生效:删配置 + 注销工具,扩展随即休眠 |
在 IDE 里禁用扩展 dshcs-editor-bridge |
桥自然不可用(工具会注册但立刻报"状态未上报";IDE 侧无任何动作) |
诊断:GET /api/code-server/status 的 bridge 字段返回
{ enabled, live, toolsRegistered, supported, url, file } —— 不含令牌(令牌只在那个文件里)。
旧版 DSH(0.2.3 起不再支持)
行为:探测不到 sidebarRightTabs / sidebarRight 时,插件只注册一张设置卡片,内容是:
Code Server — 当前 DSH 版本不受支持(缺少右侧栏服务) 本插件自 0.2.3 起不再兼容旧版 DSH。 未检测到右侧栏插件服务 sidebarRightTabs / sidebarRight,因此插件不提供任何入口(旧版的悬浮球与浮动窗口已移除), 也不会后台启动 IDE。升级 DSH 到带右侧栏的版本(≥ 0.1.5-alpha.1)后,Code Server 会出现在右侧栏标签里, 本页同时显示完整设置项;升级后无需重装本插件,刷新页面即可。
- 没有任何其他 UI:不注册
shell.overlay(悬浮球)、不认领文件地址、不做常驻预热。 - host 侧:客户端会
POST /api/code-server/ui-mode { sidebar:false };host 收到后 ① 不再自动预启动 IDE(maybePrestart直接返回),② 若 IDE 是本插件刚自动预启动且尚未被 adopt,则回收该进程, 避免留下一个用不上的 IDE 与端口。用户手动启动的实例(adopted)不会被停。 - 为什么删除而不是保留:内部浮动窗口是 2026 年早期 DSH(无右侧栏服务)时代的临时载体,
常驻面、剪贴板、快捷键、面板折叠等能力都建立在 DSH 右侧栏之上;维护两套载体的成本高于其残余价值。
旧版用户继续用
0.2.2即可(dsh plugin --profile web add dsh-code-server-app@0.2.2)。 - 回滚:任何版本都能降级到旧版实现,例如
dsh plugin --profile web add dsh-code-server-app@0.2.2。
code-server 服务目录与进程生命周期
- code-server 服务目录跟随活动工作区/会话:打开期间切换 DSH 会话/工作区,code-server 自动切到新目录
(解析优先级:当前会话 cwd → 会话所属 workspace.path → recentWorkspace.path → 首个 workspace.path);
打开目录显示在 code-server 页面内(
?folder=<cwd>,跟随切换时页面自动重新加载); 实现要点:iframe src 必须带?folder=<cwd>——code-server 前端会记住“最近工作区”并自行恢复, 仅用裸根 URL 只会显示上一次打开的目录、不会跟随切换(本机实测确认)。 Windows 路径格式(实测):folder 参数必须以/开头且全部正斜杠,形如/C:/Users/User/Desktop/biss; 裸 Windows 路径(C:\...)会被前端当 URI scheme 而剥掉盘符(页面显示\Users\User\...且文件树为空),file:///C:/...形式则报 “Workspace does not exist”。 - 切换是"轻量"的(0.2.12 起):运行中切工作区不重启 IDE 进程,host 只把
state.cwd改成新目录, 由 workbench 拿新的?folder=重新导航(工作区目录本来就由客户端 URL 决定,进程 cwd 只影响它自己 spawn 时的相对路径解析)。因此切换不再丢扩展宿主/后台任务/服务端状态,也快得多。status里cwd= 当前 workbench 目录;launchCwd= 进程启动时的目录(诊断用,不随切换变化)。- 代价(要说清楚):旧目录里由 IDE 拉起的后台进程/终端不再被自动杀掉(以前靠"整进程重启"顺带收走)—— 需要时手动收;这也是"不丢状态"的同一枚硬币。
- 触发时机与本标签是否可见无关:侧栏收起时标签 body 并不卸载,所以后台也会跟随(0.2.12 明确保留此行为)。
- 回归:
scripts/test-workspace-switch.mjs(5 项:接管实例、切目录不改 pid/不换状态、进程存活、 同目录幂等、无 cwd 不切换;改回旧行为必挂)。
- process 生命周期由 host 插件管理:启动写
$DSH_HOME/code-server/pid.json,停止树级终止(taskkill /T 或进程组 SIGKILL), 崩溃/退出实时更新状态;DSH host 重启后自动 adopt 仍在运行的实例(校验 pid + /healthz),不重复启动、不误杀别的进程; node_modules、vendor/与repack/已被.gitignore排除,推送/克隆仓库后按下方 "打包(如何出包)"执行pnpm install→pnpm run build:client→pnpm run vendor:vscode→ (发布预编译原生包)→pnpm pack+dsh plugin --profile web add即可。
本机(BM: Windows 11 ARM64)实测:树/依赖全链路是"平台子包直挂插件依赖"供给 —— 树包
@jinsiyu/dshcs-vscode-server(当前 4.137.0,50.8 MB tgz)、纯 JS 内部依赖与 8 个平台无关 重打包包直接进插件dependencies、8 个平台专属重打包包按 win32-arm64/x64 进optionalDependencies(包自带 os/cpu 自动选),原始名字由lib/native.js补 junction 还原 → healthz 200 → 停止 → 回收全链路验证。 (0.1.37 时代的主包形态已废弃,见下方"升级 VS Code 树"。)
打包(如何出包)
cd C:\Users\User\Desktop\dsh-code-server-app
pnpm install # 开发依赖(esbuild + 官方渲染器打包依赖);allowBuilds 已显式声明 → 不执行任何 postinstall
pnpm run build:client # src/factory.js → lib/client.js(不入库,必须先构建)
pnpm run build:webview # 「问 DSH」面板:官方 markdown 渲染器 + 面板外壳 → webview/thread.{js,css}(不入库,必须先构建)
pnpm run vendor:check # 可选:查看内置 VS Code 树版本 vs code-server 最新版
pnpm run vendor:vscode # ① 生成 vendor/vscode(精简 VS Code 树,≈197MB)
pnpm run repack:build -- --target win32-arm64,win32-x64 --pack # ② 统一脚本产出全部子包(见下表)
pnpm run publish:repacks # ③ 发布全部 @jinsiyu/* 子包(默认 dist-tag = next)
pnpm pack # ④ → dsh-code-server-app-<version>.tgz(约 750KB,含面板渲染器产物)
pnpm run publish:plugin # ⑤ 发布插件本体(默认 dist-tag = next)
# 用户重启 dsh web 确认无误后,再把 latest 推进到该版本:
pnpm run promote -- <version>
build:webview会把 DSH 官方的 markdown 渲染器与设计令牌打进面板产物(≈1.34MB:JS 996KB + CSS 87KB + KaTeX 字体 254KB),所以它要求本机有 DSH 部署:脚本读部署里@deepseek-ai/dsh-web-frontend的版本,与 devDependency 钉住的渲染器版本比对,不一致就报错退出(--allow-version-mismatch才放行)。 它与lib/client.js同一约定:产物不入 git,prepack里会自动重跑。 细节(为什么不是 iframe、令牌从哪来、体积取舍)见docs/analysis-code-server-as-dsh-plugin.md第 21 节。
dist-tag 政策(必须遵守):发布一律发到
next,不动latest;latest只保留「最近一个确认无 bug 的版本」,由pnpm run promote -- <version>(=npm dist-tag add dsh-code-server-app@<version> latest)在用户重启 dsh web 确认无误后才推进。 这样dsh plugin add dsh-code-server-app(不带版本)和任何按 latest 安装的流程都不会拿到未验证的版本。 子包(@jinsiyu/dshcs-*)被依赖以精确版本引用(平台专属的按目标各钉一份),dist-tag 不影响解析,但同样默认发next。 查看当前标签:npm dist-tag ls dsh-code-server-app。desktop profile 不走命令行安装(2026-09-13 起的约定):对 desktop 只做
pnpm pack+publish:plugin(发next), 由用户在 DSH Desktop 里用官方安装方式自行安装;不要再把 tarball 文件级覆盖进~/.dsh/profiles/desktop—— 那条路会绕过 desktop 应用自己的依赖闭包检查,把真实的解析问题掩盖成"装上了但行为怪"。 web profile 仍可照旧安装验证。0.3.45 起没有平台聚合包,
requires missing @microsoft/mxc-sdk@npm:…那类报错不会再出现。根因(实测): pnpm 的增量 hoisted 安装会漏链「可选子树里的npm:别名包」,16 个里漏 9 个(第一个就是 mxc-sdk),而 dsh-desktop 在pnpm add之后立刻校验依赖图 ⇒ 首次安装必失败;重启后应用走「删 node_modules + 完整安装」 才补齐 ⇒ 就是你看到的"重启自己装好了"。复现命令与两条修法见docs/desktop-first-install-root-cause.md。
repack:build(scripts/vendor-repacks.mjs)是唯一的子包产出脚本,一次生成:
| 子包 | 内容 | os/cpu |
|---|---|---|
@jinsiyu/dshcs-vscode-server@<code-server 版本> |
精简 VS Code 树(lib/vscode + out/browser + src/browser,不含 code-server 的 out/node 与 136 个运行时依赖) |
平台无关 |
@jinsiyu/dshcs-<名字>[-win32-<arch>] ×16 |
VS Code 内部依赖里需要构建的原生包(node-pty / @vscode/sqlite3 / kerberos / koffi / ssh2 / spdlog / …) | 平台专属带 os/cpu |
lib/vendored.json(不是包) |
「原名 → 重打包子包」表,随插件发布;运行时由 lib/native.js 据此补 junction。0.3.45 起不再产出平台聚合包 |
— |
argon2 已随 code-server 服务层一起移除(0.2.0):
auth固定none,需要对外访问请用serve: dsh。
| 目标 | 命令 |
|---|---|
| 打最新版(上游 code-server 发行版) | pnpm run vendor:latest(= --force):从 registry 取 code-server@latest 的树到 vendor/vscode;之后必须重跑 repack:build 并重发全部子包 |
| 指定版本 | pnpm run vendor:vscode -- --version 4.137.0 |
| 从已装好的树快照 | pnpm run vendor:vscode -- --from <code-server 目录>(秒级) |
| 开发期让树可直接跑 | pnpm run vendor:vscode -- --dev-links(额外把 lib/vscode/node_modules 用 junction 补上) |
| 完整重打子包 | pnpm run repack:build -- --target win32-arm64,win32-x64 --pack(不给 --from 会自动 npm install 解包 + 编译,耗时) |
| 只重打树包 + 依赖表 | node scripts/vendor-repacks.mjs --reuse --target win32-arm64,win32-x64 --pack(复用 repack/build 里已有的原生包,不重新分析源树;顺带重写 lib/vendored.json 与插件依赖表) |
| 发布子包 | pnpm run publish:repacks(--dry-run 预览;--only <子串> 过滤;--otp <code> / --limit N 应对 2FA) |
| 发布插件本体 | pnpm run publish:plugin(发布已验证过的那份 tarball,不会重新打包;默认 dist-tag = next) |
| 推进 latest | pnpm run promote -- <version>(用户重启确认无误后;--dry-run 先看当前标签) |
| 只报告版本 | pnpm run vendor:check |
pnpm pack的prepack会自动跑一次vendor-vscode-server脚本;vendor/vscode已存在时它是 秒级 no-op,所以日常只改插件代码的话直接pnpm pack即可(不会偷偷升级 VS Code)。 升级树必须显式pnpm run vendor:latest(或--force/--version),并重发子包。
回归脚本(改完跑一遍)
pnpm test:apply # 桩 ctx 下跑通 apply(回归:apply 期的 ReferenceError)
pnpm test:claim-types # 认领类型语法与默认值
pnpm test:bridge-routes # 编辑器桥:路由表白名单(只读 + /approve)/ Origin 与令牌的判定顺序 / 令牌头三处一致
pnpm test:bridge-extension # 编辑器桥扩展侧纯逻辑:未保存缓冲区上报、诊断排序截断、diff 判据、投递降级、面板状态机
pnpm test:webview # 面板 webview 产物:官方渲染器与令牌打包、版本一致、/approve 的四条约束(先跑 build:webview)
pnpm test:launcher-routes # launcher 的 HTTP 面(起真进程,较慢)
pnpm test:workspace-switch # 切工作区不重启进程
pnpm test:fullscreen # 打开标签即全屏
pnpm test:vendored # 重打包表 ↔ 插件依赖表一致(无 npm: 别名 / 无聚合包 / vendored.json 进了 files)
test:bridge-routes会把DSH_HOME指向临时目录(否则它会 adopt 开发机上正在跑的那个实例, 并改写真实的bridge.json);脚本最后有一条"隔离自检"断言真实配置一字未动。
安装插件(一条命令;依赖全部由包管理器装好)
# 包内无 postinstall → 无需 pnpm approve-builds / allowBuilds;一条命令装完
dsh plugin --profile web add dsh-code-server-app@0.2.1
# 本地 tarball 同理:
dsh plugin --profile web add C:\Users\User\Desktop\dsh-code-server-app\dsh-code-server-app-0.2.1.tgz
装完即用,没有第二步、没有「安装环境」、不弹安装指引。主包约 110KB(插件自身代码 + launcher), 其余全部是依赖:
- VS Code 树(
lib/vscode196.9MB +out/browser+src/browser)是一个平台无关的包@jinsiyu/dshcs-vscode-server@<code-server 版本>(0.2.0 起),写进插件dependencies,运行根在<profile>\node_modules\@jinsiyu\dshcs-vscode-server\vscode; - VS Code 内部依赖里纯 JS 的部分(35 个:xterm / katex / typescript / ws / tar …)也写在插件
dependencies,由 pnpm 装到 profile 的node_modules(hoisted); - 二进制部分全部由
@jinsiyu/dshcs-*子包提供,且直接挂在插件依赖上(0.3.45 起): 平台无关的 8 个(node-pty/koffi/ssh2/cpu-features/@parcel/watcher/@vscode/fs-copyfile/@vscode/proxy-agent/@microsoft/mxc-sdk)写进插件dependencies(真名); 平台专属的 8 个(@vscode/sqlite3/spdlog/kerberos/deviceid/native-watchdog/windows-registry/windows-process-tree/windows-ca-certs)按 win32-arm64 与 win32-x64 各一份写进optionalDependencies(真名 + 包自带 os/cpu)→ 一条命令自动选对架构; 原始名字由lib/native.js运行时补 junction 还原(见下「运行时布局自愈」); - 因此依赖图里没有任何带 pre/install/postinstall 或 binding.gyp 的包 →
不需要 profile 的
allowBuilds、不执行任何构建、使用者机器不需要 C++ 工具链; - 升级插件不再重下树:树包版本按上游 code-server 版本缓存,pnpm 直接复用(约 60MB,解包 ≈197MB)。
安装机制(为什么这样设计)
- pnpm 11 的硬约束:依赖树里任何带
preinstall|install|postinstall(或包内含binding.gyp/.hooks) 的包都被判定"需要构建",必须由宿主 profile 的pnpm-workspace.yaml用allowBuilds批准, 否则dsh plugin add直接[ERR_PNPM_IGNORED_BUILDS]exit 1。依赖包自己的pnpm.allowBuilds、.npmrc、patch:协议、optionalDependencies全都不起作用(实测 2026-09,pnpm 11.25); - 树打包期用
npm install code-server@<版本> --ignore-scripts(跳过官方sh ./postinstall.sh: Windows 无 sh,且它只认 npm/yarn 的 user-agent)拿到上游发行版,然后只保留 VS Code 树:scripts/vendor-vscode-server.mjs复制lib/vscode/**、out/browser/**、src/browser/**与许可文件到vendor/vscode/,生成树根package.json(版本 = 上游 code-server 版本,便于版本比对); code-server 自己的out/node/**与 136 个运行时依赖不再进包(由lib/launcher.mjs取代); - 需要编译的包由
scripts/vendor-repacks.mjs重打包成@jinsiyu/dshcs-*: 复制已编译的包目录 → 删除scripts/files/binding.gyp/.hooks/.npmignore(保留编译好的.node与全部运行时文件)→ 依赖里的同集包改成npm:别名 → 平台专属的加os/cpu与-<platform>-<arch>后缀;win32 目标还会校验.node的 PE machine (0x8664=x64 / 0xaa64=arm64),防交叉编译产物装错架构; - 原始名字怎么还原(0.3.45 起):重打包包的真名是
@<scope>/dshcs-<名字>,而 VS Codeimport的是node-pty/@vscode/sqlite3这类原名;打包期把「原名 → 真名」写进lib/vendored.json(随插件发布), 运行时由lib/native.js在<树>/node_modules/<原名>补 junction 指向真名包(幂等、可自愈)。 为什么不再用「平台聚合包 +npm:别名」:pnpm 的增量 hoisted 安装会漏链可选子树里的别名包 (实测 16 个漏 9 个),而 dsh-desktop 安装后立刻校验依赖图 ⇒ 首次安装必报 requires missing; 改成真名直接依赖后,同一条安装命令 + 校验器判据实测全部通过(复现见docs/desktop-first-install-root-cause.md); - 解析路径:host 用
require.resolve('@jinsiyu/dshcs-vscode-server/package.json')找到运行根 (包内子目录vscode/),入口vscode/lib/vscode/out/server-main.js;VS Code 内部依赖从该运行根向上查找 (vscode/lib/vscode/node_modules→ 包node_modules→<profile>/node_modules)。 (旧全量树@jinsiyu/dshcs-code-server/code-server仍作为回退被识别。) - 运行时布局自愈(
lib/native.js的ensureRuntimeLayout(),激活时(先于 envCheck)与每次启动前幂等执行): host 会在 VS Code 树里补两类 junction(Windows junction / POSIX 目录软链):ensureAliasLinks():按lib/vendored.json把 16 个原始名字补到<树>/node_modules—— 真名子包装在插件依赖图里,而 VS Code 的lib/vscode/out/server-main.js用 ESM import (ESM 不认NODE_PATH),缺了就直接 500;ensureInnerModuleLinks():把 VS Code 的内部依赖目录lib/vscode/node_modules与lib/vscode/extensions/node_modules按两个package.json的dependencies补回老布局 —— 精简树里没有这两个目录,用显式路径拼依赖的代码 (如内置 TS 扩展找<ext>/../node_modules/typescript/lib/tsserver.js)否则会报 「VS Code's tsserver was deleted by another application…」(1.136.1 实测)。 链接都指向包管理器装出来的真实包,树被重装后的断链会被自动清理重建;envCheck按双锚点解析, 并用NODE_PATH兜底 CJS。
体积提示:插件 tarball 约 110KB;
@jinsiyu/dshcs-vscode-server约 60MB(解包 ≈197MB); 16 个原生包合计约 250MB。全部合计安装下载约 310MB。vendor/与repack/都不入 git(见.gitignore)。
从 ≤ 0.1.43 升级:树包由
@jinsiyu/dshcs-code-server(全量 code-server,含out/node与 136 个依赖) 换成@jinsiyu/dshcs-vscode-server(精简树);新代码默认serve: loopback,行为与 0.1.43 等价, 需要同源挂载再切serve: dsh。升级命令不变(一条dsh plugin --profile web add dsh-code-server-app@<版本>),旧的dshcs-code-server子包会被 pnpm 清掉。
从 ≤ 0.1.35 升级:旧版的安装根
<profile>\.code-server-app(含约 1.4GB 内部依赖)与 「安装环境」步骤都不再需要 —— 新版本会检测到它并打一条日志提示可安全删除:Remove-Item -Recurse -Force <profile>\.code-server-app。profile 的pnpm-workspace.yaml里 若还留着dsh-code-server-app: false之类的旧条目,也可以删掉(新版不再需要任何构建许可)。
卸载:
dsh plugin --profile web remove dsh-code-server-app即可;树包与原生包 是独立依赖,若要彻底清干净可再dsh plugin --profile web remove @jinsiyu/dshcs-code-server(或直接在 profile 里pnpm remove);若还残留旧安装根,再手动删除<profile>\.code-server-app。
安装/依赖变化后请重启
dsh web(静态插件行与 host 探测路径在启动时加载)。
开发期:源码目录安装(改动即时生效)
dsh plugin --profile web add C:\Users\User\Desktop\dsh-code-server-app
源码路径以
link:安装。开发机上没有vendor/vscode时先pnpm run vendor:vscode -- --dev-links; 没有平台子包时 host 会回退到包内vendor/code-server(两种布局都支持)。 依赖(内部 JS 依赖 + 重打包子包)同样由 pnpm 安装 —— 本地未发布的@jinsiyu/*需先发布, 或把repack/tgz/*.tgz以file:依赖临时装进 profile(见.tmp-verify.mjs)。改动 client bundle:编辑
src/factory.js后执行pnpm run build:client重新生成lib/client.js(仓库不跟踪该产物;浏览器刷新即生效,host 无需重启)。 改动提问面板:编辑assets/extensions/dshcs-editor-bridge/webview/src/*后执行pnpm run build:webview(同一约定:产物不入库;IDE 需重启一次才会加载新产物,扩展宿主会缓存 webview 资源)。
打包机环境要求(使用者机器什么都不需要)
工具链只在打包期需要;使用者机器不需要 C++ 工具链,也不需要联网装依赖之外的任何东西。
| 环境 | 版本/要求 | 使用者机器 | 打包机 |
|---|---|---|---|
| Node.js | v24.x(code-server 最新要求;本机 v24.13.1) | 必需 | 必需 |
| npm / pnpm | npm 跟随 Node;pnpm 由 DSH 提供 | 必需(装依赖) | 必需 |
| MSVC 构建工具 | VS Community 2026 + C++ 桌面负载 | ❌ 不需要 | 打包期需要(编译 16 个原生包) |
| VS Spectre 缓解库 | ARM64 与 x86/x64 各一份("MSVC v14x Spectre-mitigated libs") | ❌ 不需要 | 打包期需要(否则 MSB8040) |
| Python | 3.13.x | ❌ 不需要 | 打包期需要(node-gyp) |
| node-gyp | 13.x(旧版不识别 VS 2026) | ❌ 不需要 | 打包期需要 |
缺 Spectre 库也能出包:
vendor-repacks.mjs在某个包编译失败时会自动把该架构*.gyp里的SpectreMitigation降级为false并重试(只是少了 Spectre 加固,功能不受影响),日志会明确提示。
Windows 原生构建要点(打包期,本机实测 ARM64)
- VS 需 Spectre 缓解库组件(MSB8040):Visual Studio Installer → 单个组件 → "MSVC v14x Spectre-mitigated libs",ARM64 与 x86/x64 要分别安装(只装一个架构会缺另一个)。
- node-gyp 13.x(旧版 9.x 不识别 VS 2026):
npm install -g node-gyp@latest。 - x64 交叉编译:
vendor-repacks.mjs用npm install --os=win32 --cpu=x64 --ignore-scripts取包,再npm rebuild --arch=x64逐个编译,已验证产物 PE 架构正确(kerberos / sqlite3 / spdlog …)。 - code-server 最新版要求 Node v24。
- 若不需要插件自足(例如已有全局 code-server),可跳过安装:
插件会回退到 PATH/配置的
bin(见"配置"表)。
升级 VS Code 树(上游 = code-server 发行版)
- 打包期决定版本:
pnpm run vendor:latest(=--force)取 npm 最新版 code-server 的树并重建vendor/vscode; 也可pnpm run vendor:vscode -- --version 4.137.0或设DSHCS_CODE_SERVER_VERSION。 已有vendor/vscode时,不带--force/--version不会升级(日常pnpm pack是 no-op)。 源树优先级(0.2.13 修正):显式--from用给定树;显式--force/--version一定走 registry —— 修正前它们会被"本机已有源树"抢走(defaultSourceTree()先命中vendor/code-server或 profile 里的旧树), 于是 README 写的"vendor:latest从 registry 取 latest"实际拿不到新版本(0.2.13 升级时踩到:内置树一直停在 4.136.2); 只有既没--force也没--version时才复用本机源树省一次下载。 - 换版本时同步树内依赖 pin:
--reuse模式下纯 JS 直装集是从插件 package.json 现读的, 所以要先按新树的lib/vscode/package.json更新 pin(本次 10 个@xterm/*的 beta 跳号; 判定规则是"现有 pin 不满足新范围才动",避免把cookie/ws/tar/node-addon-api这类已有更高版本降级)。 - 先查再升:
pnpm run vendor:check打印「内置版本 / 上游 latest」。 - 换版本后重新出子包并发布(全部由同一个脚本):
pnpm run repack:build -- --target win32-arm64,win32-x64 --pack→ 新的树包 (@jinsiyu/dshcs-vscode-server@<新版本>)以及按新内部依赖重建的原生包 (脚本会把新的「纯 JS 直装集」写进插件dependencies,并把 16 个重打包包按真名写进dependencies/optionalDependencies、重写lib/vendored.json);pnpm run publish:repacks→ 发布;然后 bump 插件版本 →pnpm pack→ 发布插件。
productPath(<quality>-<commit>,客户端 WS 路径的组成)从lib/vscode/product.json现算, 升级树后无需改代码 —— 但也意味着切版本后必须重启 dsh web(路由在激活期注册)。- 不再有运行期自动升级:不会在启动时联网取 latest;版本完全由内置产物决定。
- 本机当前内置:
code-server@4.137.0的树(VS Code 1.137.0,productPath=stable-b11dabda…)。
兼容旧安装位
host 探测顺序:@jinsiyu/dshcs-vscode-server/vscode(0.2.0+ 正式布局)> @jinsiyu/dshcs-code-server/code-server
(0.1.40–0.1.43 全量树)> @jinsiyu/dshcs-code-server-<平台>-<架构>/code-server(0.1.37 平台专属子包)>
插件包内 vendor/vscode > 插件包内 vendor/code-server(开发期)。旧安装根
<profile>\.code-server-app 只在启动日志里提示可删除,不再被使用。
设置卡片(设置 → 插件 → Code Server)
参照 dsh-auto-open-web 的自绘卡片模式,注册在 settings.plugin.item 插槽,
数据经官方 settings 域(settingsScope,命名空间 code-server)持久化到官方 settings 文档:
| 键 | 默认 | 说明 |
|---|---|---|
claimExtensions |
*;!md;!markdown;!html;!htm;!png;!jpg;!jpeg;!gif;!webp;!bmp;!ico;!svg;!pdf |
认领类型(0.2.11,取代 0.2.5 的 fileOpenScope):按扩展名决定哪些文件交给 VS Code,分号分隔;* = 其余类型也认领,!ext = 不认领(排除优先)。默认把 DSH 预览渲染得好的四类(markdown/html/图片/PDF)留给它,其余全进 IDE;清空 = 不认领任何文件。不再区分 session/absolute 作用域 |
fullscreenOnOpen |
true |
打开即全屏(0.2.9):打开 Code Server 标签(含点开文件)时自动把右侧栏切到全屏(铺满窗口);关闭则保持 DSH 默认的 push(与对话并排)。只影响打开那一刻,用户点「退出全屏」不会被抢回去 |
keepResident |
true |
后台常驻:开启后宿主启动即把 IDE 预加载到"停放区",切标签/收起侧栏不重载、首次打开免等待;关闭则只在打开面板时加载(省内存) |
(0.2.9 起卡片只留上面三个设置;windowedOpen 与 reserveComposer 已移除 —— 旧设置文档里残留的键既不报错也不生效。
serve 仍是设置命名空间里的键(便于用设置文档切换),但没有卡片行,见「服务方式」。)
0.2.7 起卡片没有「入口」「依赖安装」「环境检测」三行:入口在右侧栏「开始」页的 Code Server 入口框(或官方的文件点击),
诊断信息不再进 UI —— /api/code-server/status 的 env 字段仍返回
树版本 / productPath / server 入口、VS Code 内部依赖、预编译原生包(重打包子包名 + 已解析模块数),需要时用脚本查或看 host 日志。
卡片改动经
scope.watch实时生效(host 端 status API 同步返回keepResident、claimExtensions与fullscreenOnOpen,客户端立即生效);无需重启 dsh。新增设置键后首次使用前需重启 dsh web, 让 host 重新注册设置命名空间(schema 含新键),否则新键的保存与校验不生效。
配置(cordis.patch.yml 的 config,均有默认值)
| 键 | 默认 | 说明 |
|---|---|---|
serve |
loopback |
服务方式:loopback(独立回环端口,iframe 跨源)→ dsh(挂到 DSH 自身端口的 /code-server/*,转发到命名管道,复用 DSH 的 Host/Origin + cookie 防护)。需 DSH 提供 webServer,缺失时自动回退 loopback |
bin |
''(空 = 用自带 launcher) |
逃生舱:显式指定外部 code-server 可执行文件 / out/node/entry.js 时退回旧模型(不经 lib/launcher.mjs) |
host |
127.0.0.1 |
loopback 模式的绑定地址(仅允许回环) |
port |
0 |
loopback 模式的端口;0 = 每次启动由系统分配随机端口(实际端口写在 endpoint.json,host 读回)。显式给端口则固定使用;该端口被占用且无有效 pid.json 时报错并给诊断(拒绝误杀) |
auth |
none |
固定 none(0.2.0 起 argon2 已移除);回环模式的访问控制由随机端口 + 路径令牌 + Host 白名单承担(见「回环端口的安全模型」),对外访问请用 serve: dsh |
userDataDir |
$DSH_HOME/code-server/user-data |
用户数据隔离目录 |
extensionsDir |
$DSH_HOME/code-server/extensions |
扩展目录 |
locale |
'' |
界面语言(空 = 跟随浏览器),如 zh-cn |
readyTimeoutMs |
60000 |
/healthz 就绪探测超时(TCP 或命名管道) |
editorBridge |
true |
编辑器桥(0.3.0 起):树内扩展 dshcs-editor-bridge 与 host 之间的只读通道(见「与 DSH 的协同」)。关掉 = 不写 bridge.json、不注册 editor_context/editor_diagnostics、扩展休眠。设置文档里的 code-server.editorBridge 可即时开关 |
用户级覆盖示例(写在 $DSH_HOME/profiles/web/cordis.patch.yml,应使用 - id: code-server 行覆盖):
- id: code-server
config:
serve: dsh # 同源挂载:/code-server/*(无额外端口,复用 DSH 防护)
# port: 8091 # serve: loopback 时才生效
JSON API(同源 fetch;web 与 desktop 同一套路径)
不依赖 webServer:host 半部经 ctx.connection.fetch.register 把路由挂在 DSH Connection 的共享 /api 通道上——
web profile 由 Connection 自己把 /api 前缀挂到 webServer(带 Host/Origin 校验 + 浏览器鉴权),
desktop profile 由 apps/desktop-host 把 /api/* 交给同一个 createSharedFetchHandler('/api')(IPC 帧管道,无 HTTP 服务器)。
客户端只写相对路径 fetch('/api/code-server/<op>'),两端行为一致。
| 方法 | 路径 | 说明 |
|---|---|---|
| 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 = 含路径令牌的完整地址) |
| POST | /api/code-server/start |
body { cwd? }(省略 cwd 不切换工作目录);幂等;运行中切 cwd = 只换目录不重启进程(0.2.12);loopback 新启动会轮换端口与路径令牌(0.2.14) |
| POST | /api/code-server/stop |
停止并回收进程树 |
| POST | /api/code-server/setup |
兼容空操作:0.1.36 起依赖由包管理器安装,调用只重新自检 env 并返回 |
| POST | /api/code-server/open-file |
body { file } — 写信号文件,由内置扩展 dshcs-open-file 在 code-server 中打开 |
| GET | /code-server-bridge/health |
编辑器桥探活(无鉴权;只回答"桥活着吗",不含任何编辑器数据)。走本机 IPC(命名管道 / unix socket),不在 /api 下、也不需要 webServer |
| POST | /code-server-bridge/sync |
编辑器桥:扩展上报状态({context, diagnostics, workspace, at})并取回事件;?since=<seq> 是事件游标。需 x-dshcs-bridge-token,带 Origin 一律 403 |
| POST | /code-server-bridge/ask |
编辑器桥:把编辑器里的提问投进当前会话({text, file?, lineStart?, lineEnd?, selection?, languageId?});没有可投递的会话时回 409 |
| POST | /code-server-bridge/event |
编辑器桥:扩展上报打开/关闭文件等(进 host 日志尾)。需令牌 |
桥的四条路由都自带令牌鉴权(它们不依赖 DSH 的 cookie fence —— 扩展宿主拿不到浏览器 cookie), 且永远只读。传输是本机 IPC(0.3.13 起),所以 web 与 desktop 同一套: 端点由 host 写在
bridge.json的pipe字段里,扩展用http.request({ socketPath })访问。
除桥之外,插件不再注册任何插件自有 HTTP 路由;code-server 图标已内联为 data URI(client bundle 内), 因此客户端不请求任何插件自有 HTTP 资源。
DSH Desktop(无 webServer)
- host 半部
inject = ['connection', 'settings'](不含webServer)——desktop profile 关掉了 webserver/web-runtime, 本插件照常工作;/api/*请求由 Electrondsh-app://协议处理器 → IPC 帧管道 →createSharedFetchHandler('/api')。 - 右侧栏标签、guide 入口框、文件地址认领、设置卡片在 desktop 下与 web 相同(code-server 仍是本机
http://127.0.0.1:<port>的 iframe; 桌面端webSecurity: true且页面无 CSP 限制,跨源 iframe 正常加载)。 桌面端同样自带dsh-client-ui-sidebar-right(见 desktop 构建 seed 包列表),因此 0.2.3 的 "只支持带右侧栏的 DSH" 对 desktop 不构成降级;serve: dsh会自动回退 loopback(那条路确实需要 webServer)。 - 编辑器桥在 desktop 下可用(0.3.13 起):桥走本机 IPC(命名管道),与
webServer无关 —— 扩展宿主是插件自己 spawn 的 IDE 的子进程,两端都在同一台机器上。host 注入DSHCS_EXTENSIONS_DIR后 扩展即可找到bridge.json;/status的bridge.supported/endpoint在 desktop 下同样是true/管道名。 - 安装到 desktop profile:桌面端插件管理窗(不是 CLI,见下)。
- 桌面端安装的 24 小时供应链策略(实测,2026-09-10,已用它装上 0.2.4):
- CLI 路径不可用:
dsh plugin --profile desktop …会被拒绝("profile "desktop" is managed exclusively by the Electron application"), 桌面端只能走应用内的包事务(pnpm add <spec> --save-exact,在~/.dsh/desktop/staging/<uuid>/profile里执行后激活)。 - 该事务用的 pnpm(应用自带 11.7.0,DeepSeek 打过补丁)在
add前先做锁文件供应链校验: "Verifying lockfile against supply-chain policies (717 entries)",默认要求发布满 24 小时, 否则ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION。 - 关键区别(两者行为不同,实测):
- 校验已有锁文件时:不接受
minimumReleaseAgeExclude(精确版本、裸包名都试过,不放行); - 解析(没有锁文件可校验时,如
pnpm clean --lockfile之后):认这份名单,而且 pnpm 自己会往pnpm-workspace.yaml追加条目(安装日志会打印 "Added N entries to minimumReleaseAgeExclude…")。
- 校验已有锁文件时:不接受
- 因此桌面端装刚发布(<24h)版本的可行路径是 从"没有锁文件"的干净起点安装:
pnpm clean --lockfile(注意:它会连node_modules一起删,profile 变成待重装状态);- 用应用自带的 runtime 在 profile 目录里
add <spec> --save-exact --trust-lockfile(运行时/仓库/配置目录都在~/.dsh/desktop/pnpm/{store,cache,state,config,home},--config.userconfig=…/config/npmrc, 否则会出现ERR_PNPM_UNEXPECTED_STORE/…UNEXPECTED_VIRTUAL_STORE); - 安装后应用启动用的
install --offline --frozen-lockfile --trust-lockfile可正常通过(锁文件与 package.json 一致、 包已在 store);dsh.profile.bundles里已有插件名时不需要再改。
- 不要用
minimumReleaseAge: 0绕过:它确实能让校验通过,但那个键不在应用容忍的 policy 段里 (project-manager.ts只忽略minimumReleaseAgeExclude:/trustPolicyExclude:,且每次mutate()前都会校验), 写进去会让应用报 "core package mapping does not match desktop-packages.json"。 - 也可以选择 等满 24 小时再在插件管理里正常安装;web profile 不受影响(它的
pnpm-workspace.yaml是minimumReleaseAge: false)。 - 本插件的依赖闭包里含平台原生子包(
@jinsiyu/dsh-code-server-runtime-win32-*),它们与插件本身同批发布, 因此每次新版本在桌面端都会受这条策略约束。
- CLI 路径不可用:
- 桌面端的客户端 bundle 会被 Electron 缓存,重启应用不保证换新(2026-09 实测,0.2.5 踩到):
- 现象:profile 里
lib/client.js已是新版本,但渲染器仍跑旧代码——%APPDATA%\@deepseek-ai\dsh-desktop\Code Cache\js里只有旧版本独有的字符串(如dshcs-artifacts),新版本独有的字符串(如claimExtensions/fullscreenOnOpen)一个都没有;Cache\里也存着一份引用dsh-code-server-app的旧响应体。官方插件同理(缓存里那份ui-deliverables连当前的data-presented-files-row都没有)。 - 诊断手法(字节级,注意别用按控制台编码读文件的
Select-String,中文标记会假阴性): 在Code Cache\js里搜本版本独有的ASCII 标记(0.2.11 起用claimExtensions),命中即说明新 bundle 真的被编译过。 - 修法:完全关闭应用后删
Cache、Code Cache、GPUCache三个目录再启动(只清缓存,不动 profile/会话/设置):Remove-Item -Recurse -Force "$env:APPDATA\@deepseek-ai\dsh-desktop\Cache","$env:APPDATA\@deepseek-ai\dsh-desktop\Code Cache","$env:APPDATA\@deepseek-ai\dsh-desktop\GPUCache" - 影响面:不只本插件——任何客户端插件升级后都可能继续跑旧代码;发布后请按上面的标记法确认渲染器真的换了 bundle。
- 现象:profile 里
已知限制
编辑器桥需要 DSH 提供已不成立(0.3.13 修正):桥改走本机 IPC (Windows 命名管道 / unix socket,webServerhttp.request({ socketPath })),web 与 desktop 同一套, 不需要webServer、也不开端口。历史:0.3.9–0.3.12 挂在 DSH 的 webServer 前缀下 ⇒ desktop 永远休眠; 0.3.7 及以前挂在/api/code-server/bridge/*⇒ 被 Connection 的 cookie fence 401 挡死。 文件打开从来不受影响(它走信号文件)。/code-server-bridge/health的bridge字段不代表扩展在跑(0.3.12 澄清):它只表示"桥的目标已就绪"。 扩展是否真的在跑,看 exthost 日志里有没有它的激活记录,或直接用editor_context试一次 —— 0.3.0–0.3.11 就是"health 说 bridge:true、扩展却从没被加载"的状态(原因见上:用户级安装被标.obsolete)。桥的状态有最多 600ms 滞后:扩展每 600ms 推一次;超过 10s 没更新时工具会明说"状态已过期" 而不是拿旧数据当新数据(例如用户在 IDE 里关掉面板之后)。
未保存缓冲区是"上报"而不是"接管":agent 仍然通过它自己的
fs工具按磁盘内容编辑。 桥能做的是在写之前提醒、写之后给 diff、冲突时告警而不覆盖 —— 它不能替用户决定保存与否(那需要改动 agent 的读路径,不在本版本范围内)。提问面板只渲染"新内容"(0.3.22):订阅从面板建立那一刻开始,
follow开帧里的历史records被丢弃, 面板里没有"加载更早"(历史分页 APIsessionController.page()在这个版本里刻意不调用)。 想看更早的内容请回 DSH 界面。面板的高亮只带 DSH 启动集的三套语法(typescript / shellscript / json):官方其余语法走懒加载 (按需
import(),合计约 1.6MB),面板是单文件 IIFE、没有按需加载,所以那些语言的代码块纯文本显示 (与 DSH 首次渲染时的样子一致,不报错)。要全量:node scripts/build-webview.mjs --all-grammars。面板产物与 DSH 版本绑定:渲染器按构建时 DSH 部署的界面版本打包,DSH 升级后要重打面板 (
pnpm run build:webview;构建脚本会在版本不一致时直接报错)。运行期面板顶部也会提示版本不一致, 不会悄悄用错版本的渲染器。面板里的授权窗口是 5 分钟:面板打开着的时候授权先问面板(卡片上有倒计时);关掉面板或等满 5 分钟 就交回 DSH 界面 —— 交回之后这一条只能在 DSH 界面里处理(卡片从面板消失,对话流里留一行授权审计)。
子路径不支持已不成立(0.2.0 实测更正):VS Code 渲染出的 workbench HTML 里 资源引用全是相对路径(实测 9 条引用中绝对路径 0 条,serverBasePath="."、rootEndpoint="."), 客户端 WebSocket 路径由location.pathname + join(serverBasePath ?? '/', <quality>-<commit>)拼成, 因此可以直接挂在 DSH 自身的/code-server/*下(serve: dsh),不需要独立端口、 也不需要改写 HTML。逐项证据见docs/analysis-code-server-as-dsh-plugin.md。serve: dsh的端口转发 WS 不可用:registerUpgrade是精确路径匹配,而/proxy/:port的端口号在路径里 → 该模式下 Ports 面板的 WebSocket 转发不可用(HTTP 转发正常);需要时用serve: loopback。serve: dsh的 iframe 与 DSH 同源 → 该模式不挂sandbox(同源 +allow-same-origin可被 frame 自行摘除);loopback模式跨源,sandbox作为真防护保留。跨会话单实例:host 级共享一份 IDE;切换 cwd 只换 workbench 目录(0.2.12 起不重启进程,旧目录的后台终端不会被收走)。
旧版 DSH 不受支持(0.2.3 起):没有
sidebarRightTabs/sidebarRight的 DSH 上,除设置页一条升级提示外无任何入口; 旧版用户请留在0.2.2(dsh plugin --profile web add dsh-code-server-app@0.2.2)。侧栏标签切换(0.2.2 起不再重载):DSH 右侧栏只渲染当前激活标签的 body,React 卸载会移走 iframe; 插件把 iframe 收成单例常驻面,用
Element.moveBefore()(状态保持型原子移动)在停靠位与文档级停放区之间搬, 切标签/收起侧栏再回来不重载。不支持moveBefore的浏览器退回旧行为(appendChild→ 整页重载), 状态里以degraded明示;详见下方「为什么切标签不再重载」。远程访问:
serve: dsh下浏览器只需能到达 DSH 本身(单一端口,认证与/api同级);serve: loopback仅回环绑定(随机端口 + 路径令牌 + Host 白名单,见「回环端口的安全模型」), 跨机访问请改用serve: dsh(0.2.0 起不再支持auth: password)。回环模式的令牌会随实例轮换:每次新启动端口与令牌都变;
adopt(host 重启后接管存活实例)靠endpoint.json+path-token两个文件对上,所以别手动删这两个文件(删了 host 认不出旧实例, 会当成陌生端口占用处理)。
chuspeeism/dashi-taskboard
ccch1mneyyy/working-activity
Aisland-SJL/dsh-worktable
zhoushoujianwork/easyeda-agent
morluto/rea
linhay/harmony-next.skills