DeepSeek Harness Hub
← 返回列表

VS Code 侧边栏chendefine/dsh-sidebar-vscode

DeepSeek Harnessspec-screened在 GitHub 查看 ↗
✓ 可直接安装

在侧边栏内嵌 VS Code,把选区与文件变成引用 chip

自动检查通过:npm 包已发布且 engines 声明满足基线(声明 Node ^22.19.0 || >=24.0.0);该结论来自程序自动检查,未经人工实机验证。 · 最近上游提交 2026/9/15 · 已提供中文文档

DSH 插件:一个更好的侧边栏标签页,在会话工作区中嵌入 VS Code Web 工作台;编辑器选区和资源管理器文件会以原子引用芯片的形式呈现

综合分
34.9
GitHub 分
34.9
用户评分
★ Stars
0
周下载量
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add dsh-sidebar-vscode
npm 包 dsh-sidebar-vscode 已校验归属本仓库,走 npm 安装最省事
数据截至 2026/9/16(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
安装兼容性检查✓ 自动检查通过

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

npm 包dsh-sidebar-vscode @ 0.3.2
Node 引擎要求 ^22.19.0 || >=24.0.0 · 基线 Node 22.19 满足
dsh CLI 依赖未声明 dsh 版本约束
入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后验证 2026/9/18 02:58:22

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-agent@deepseek-ai/dsh-attachment@deepseek-ai/dsh-brand@deepseek-ai/dsh-invariants@deepseek-ai/dsh-llm@deepseek-ai/dsh-timeout@deepseek-ai/schemastery@deepseek-ai/dsh-client-locale@deepseek-ai/dsh-client-runtime@deepseek-ai/dsh-client-ui-conversation@deepseek-ai/dsh-client-ui-input-trigger
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

dsh-sidebar-vscode

English · npm · GitHub

为 DeepSeek Harness(DSH)官方右侧边栏注册一个内嵌 VS Code 网页版 的标签类型(ctx.sidebarRightTabs + @deepseek-ai/dsh-client-ui-sidebar-right 的 sidebar.right.pane.tab 席位),并把编辑器选区 / 资源管理器文件变成对话输入框里的原子引用 chip——提交时由 host 半展开为紧随引用消息之后的模型上下文。用户设置位于官方插件配置卡片:设置 → 插件 → 插件配置 → VSCode 侧边栏。

npm license node CI stars

界面截图

产品使用截图

编辑器选区                          资源管理器
右键 / Ctrl+Alt+C                  右键文件 / 文件夹
│                                  │
└──────────► 原子 chip ◄───────────┘
@src/main.ts L10-L12   @src/main.ts   @src
│                                  │
▼ 提交时                           ▼
/
(携带捕获快照与时效标记)          (仅路径,无内容)

- 包名:dsh-sidebar-vscode(npm)
- 源码:chendefine/dsh-sidebar-vscode(GitHub)
- 版本:0.3.2
- 许可证:MIT
- 平台:web(DSH Web GUI)
- 测试:511 例全部通过(24 个规格文件)

功能简介

标签页

- 在官方右侧边栏注册页面标签类型 vscode(两阶段注册:静态定义进 ctx.sidebarRightTabs(含引导页入口框),正文进 keyed 的 sidebar.right.pane.tab 席位),以同源 iframe 内嵌 code serve-web 工作台,并自动定位到当前会话的工作区(/?folder=)。官方 pane 只渲染活动标签的正文,而 iframe 一旦离开父节点浏览上下文即被销毁(HTML 规范:removing steps 销毁 child navigable;Chromium 151 实测同 document 内搬家也会整帧重载),因此 workbench 并不住在标签正文里:workbenchRuntime.ts 把它放进 document.body 下的常驻宿主、永不离开 DOM,正文只渲染占位符,由投影器把宿主盒子钉在占位矩形上(见技术架构「常驻工作台」一节)。同 pane 切走再切回零成本——帧、WebSocket、编辑器状态全程在线;工作区 / serverUrl / pathMap 变化原地重载;关闭标签(记录移除,即 signal abort)销毁工作台,插件卸载亦然;
- 工具栏显示工作区路径,提供「刷新」「在新窗口打开」;外观跟随 DSH 亮 / 暗 / 系统主题;界面文案中英双语。

引用注入

- 选中代码引用:在嵌入的 VS Code 里选中代码,右键「DSH: 发送选中代码到会话」(英文界面:DSH: Send Selection to Session)或按 Ctrl/Cmd+Alt+C,选区落为对话输入框当前光标处的一条不可拆分的原子 chip(形如 @src/main.ts L10-L12,退格一次删整条;输入框如有选区则替换该选区);多光标选区按编辑器顺序逐条成 chip。提交时 host 半在 agent/pre-step 把它展开为紧跟该消息的独立 context 消息:

const a = 1
const b = 2
const c = 3

- 资源管理器文件 / 文件夹引用:资源管理器右键「DSH: 发送文件到会话」「DSH: 发送文件夹到会话」(英文界面:DSH: Send File/Folder to Session;支持多选与混选,按右键对象是否为文件夹二选一显示),每个选中项落为输入框当前光标处的一条原子 chip:文件 @src/main.ts、文件夹 @src。资源引用不携带任何内容——提交时展开为只有路径属性、无正文、无提示注释的标记,tag 名本身表达文件 / 文件夹类型,模型需要时自行读取:

- 引用管理:输入框上方渲染引用 tag 栏——同一引用的多条 chip 归并为一枚 tag(显示截断 / 文件夹徽标与出现次数),点 × 经一次草稿写入移除该引用的全部 chip;chip 的序列化形态是自包含的规范 mention,草稿文本即唯一存储,删光即不再注入,无残留状态;

- 降级与恢复:直连跨域 serverUrl 时同源桥不可用,信封落入真实剪贴板,粘贴进输入框仍被识别为 chip 并落在粘贴光标处;chip 插入被输入机拒绝(提交中等瞬态)时退化为追加纯文本 mention(host 解析路径相同,仅失去 chip 外观);从对话气泡 / 外部编辑器复制渲染出的引用再粘回——即使是 sigil 被空白撑开的散架文本(@  label )或丢失闭合括号的截断复制体——经 canonical 校验后在光标处重建为原子 chip,前后散文保持原样(fail-soft,绝不报错);

- openAsDefault 开关只控制下面三个文件打开接管(better-sidebar 时代的「新会话种子交换」随该体系一同移除——官方侧栏种子是引导页、布局仅内存态、openTab 必定展开列,新会话的发现路径就是引导页入口框)。

- 右侧边栏按钮接管(同一开关):会话头部的「打开右侧边栏」按钮(折叠态的展开控制,官方 ExpandButton,唯一带 data-sidebar-right-expand 标记的节点)原本把列展开到上次停留的标签——新表面的种子是引导页(多条引导入口并存时官方种子规则永远解析到 guide)。开关开启时,本插件在 document 捕获阶段截获该按钮的点击并改发 openTab('vscode'):揭示/新建会话唯一的 workbench 页标签并同一步展开列(打开自带的展开恰好覆盖按钮原要执行的 setExpanded(true);只有打开成功后才 stopPropagation + preventDefault,失败则点击原样落回官方展开)。按钮直连每会话 store、无公开服务接缝,DOM 捕获是唯一稳定切入点;开关每次点击现读,关闭即完全恢复官方行为。

- 对话文件点击接管(同一开关控制):对话里点击变更文件标签(每轮结束的 produced-files chips)、工具行路径链接或正文文件引用时,改在内嵌 VS Code 里直接打开该文件——官方运行时把这些打开全部路由到 ctx.sidebarRight.openResource 公共漏斗(ui-chat 注入的 openFile、deliverables 行的 chips、正文文件链接),本插件在 controller 实例上包装这一个接缝(原型方法之上的自有属性 shadow;卸载时仅当阴影仍属自己才还原)。包装器解析 dsh-resource://file/session// / …/absolute/ 地址、解析会话工作区根,把打开翻译为 openTab('vscode', { params: { path, line? } }) —— 侧栏同一步展开并揭示唯一的 workbench 标签,文件经 tab.navigation(带单调 revision 的 params——天然的单发命令载体)落到其内部。文件类型拦截(openBlocklist):命中黑名单后缀(默认 pdf/docx/xlsx/pptx/png/jpeg/jpg,可在设置卡增删)的文件不进 VSCode——调用原样落回官方路由,由官方注册表的查看器认领(今天是内置文本预览;未来的查看器插件会注册自己的官方类型)。拒绝路径同理:开关关闭、非文件地址、解析失败、会话根未知都原样放行,且每次点击现读设置。认领后的链路:正文侧的 navigation 消费者 → 扩展 spool(/tmp/dsh-sidebar-vscode//cmd.json,500ms 轮询消费、showTextDocument)——cap.json 活性标记 + 能力探测把门,任一失手降级为 URL payload 参数整页重载一次。启动标签投递(扩展 ≥ 0.1.3):命令等本次启动的 nonce 落盘后才发送,并把 nonce 作为启动标签写进命令(cmd.json {boot});只有以同一 nonce 启动的扩展宿主才会消费带标签的命令(残留宿主不得把命令吃进垂死窗口)。开关关闭 = 完全不启用(chat 行为零变化)。

- 设置页「打开配置文件」接管(同一开关):按钮原本把 $DSH_HOME/settings.yaml 交给系统原生打开器——headless 容器上直接失败(xdg-open 缺失);当前运行时上点击走 remote.settings.openSettingsDocument 宿主 Remote(SettingsDocumentStore.open 是唯一生产调用方;包装器重定义该命名空间方法的 getter-only 自有属性,经嵌套可选 inject 安装——子 fiber 等到 remote.settings 服务出现才运行),gateway 之前的运行时则走旧版 /api/settings.openDocument 成员——两者恰好只会拦截到一个。开关开启时,本插件改走自有的受信围栏路由(POST /sidebar-vscode/api/settings.document → prepareDocument())取到文档绝对路径,再以 openTab('vscode', { params: { path } }) 改道——与对话点击同一条导航通道,配置文件在内嵌 VS Code 里打开(绝对路径无需命中 pathMap 规则,mapPathForOpen 对未匹配路径原样透传)。改道落地后「设置」弹框也会自动关闭:弹框开启状态是组件本地 state(没有服务暴露关闭方法),关闭走弹框自身挂在 document 上的 Escape 监听(生命周期恰好等于弹框开启期)——合成一次 Escape 键事件即可,视野留给工作台。全程 fail-soft:settings 服务缺失、host 半未重载、任何传输错误都回退到原生打开(弹框不关),按钮不会因本插件而坏。

安装方法

前提

- Web 应用带官方右侧边栏的 DSH 宿主(@deepseek-ai/dsh-client-ui-sidebar-right;sidebarRightTabs / sidebarRight / settingsScope 服务自 0.1.5-alpha.1 起存在——会话头部的「展开侧栏」按钮即标志)。缺席时客户端 fiber 静默等待、什么都不注册;
- 一个浏览器可达的 code serve-web 实例。接入形态按部署环境任选:
1. 内置反代(默认,Windows / 局域网直跑 dsh web 首选)——serverUrl 留空即默认 http://127.0.0.1:8000(本机裸启动 code serve-web 的完整地址),或直填 serve-web 输出的任意完整地址(可含基路径与 ?tkn= 令牌)。地址经 /sidebar-vscode/api/proxy.config 推给宿主半,在 dsh web 自己监听的端口上挂载为同源 /sidebar/vscode/(HTTP 透传 + WebSocket 管道 + 令牌自动附加),零 nginx、零启动参数:
code serve-web            # CLI 默认:8000 端口、根路径

路由以首页 HTML 烘焙的真实 serverBasePath 自动校正——serve-web 加不加 --server-base-path 都行(非挂载点基路径注册恒等镜像,根路径注册 - 补片)。宿主暂时探不到该地址时自动回退直连 iframe 并提示,代理就绪后自动切回挂载点。免配置预启用可设 DSH_SIDEBAR_VSCODE_UPSTREAM(同样接受完整地址;默认 http://127.0.0.1:8000;off 关闭)。/sidebar/vscode 被其他插件占用时仅告警退出,不影响插件其余功能;
2. 网关同源反代(参考拓扑)——serve-web 与 dsh-runtime 同容器,经网关子路径 /vscode 反代(见部署拓扑);同机部署留空 serverUrl 也可(内置反代接管)。仅当 serve-web 不在 DSH 宿主可达范围内时才需显式配置(serverUrl 填 /vscode 或预配置环境变量指向真实地址);
3. 跨域直连(自动降级形态)——仅当宿主半无法反代时自动出现:同源剪贴板桥不可用(粘贴兜底仍在),选区发送走「复制 → 粘贴」链路;
- 配套 VS Code 扩展 dsh.selection-reference 已装入该 serve-web 实例(提供右键命令与快捷键;对话文件点击打开通道需 ≥ 0.1.2,见下)。

插件本体

通道 A —— bundle 通道(标准,干净 profile 推荐)

包内声明了 dsh.bundle.patch(cordis.patch.yml:一条 insert 行,挂载 host 半 entry)。从 npm registry 安装(预构建产物,无需构建许可):

dsh plugin --profile web add dsh-sidebar-vscode

从 GitHub 仓库安装(源码——pnpm 会执行 prepare 构建;仓库同时带有已提交的 lib/ 产物兜底):

dsh plugin --profile web add github:chendefine/dsh-sidebar-vscode

或经 DSH 插件市场(设置 → DSH插件市场)——给仓库打上 dsh-plugin topic 即被自动收录。

bundle 插件加入 profile 层叠后需重启 dsh web 才加载;卸载用 dsh plugin --profile web remove dsh-sidebar-vscode,再重启一次。

通道 B —— link + 手动挂载行(本部署的热通道,免重启)

1. 把插件以 link: 依赖装进 web profile
( 为仓库检出路径,如 /opt/dsh/plugins/dsh-sidebar-vscode;
本部署 profile 目录为 /data/dsh-home/profiles/web)
pnpm -C  add link:

2. 在 profile 自己的 cordis.patch.yml 追加挂载行
- insert:
- id: dsh-sidebar-vscode
name: dsh-sidebar-vscode

watchUserPatches 热挂载 node 半,client boot graph 实时重算,/plugins/dsh-sidebar-vscode/client.js 立即可服务——浏览器硬刷新(Cmd/Ctrl+Shift+R)即可看到新标签。

⚠️ 两个通道互斥:包内 bundle 行与 profile 手动行使用同一 entry id dsh-sidebar-vscode,同时存在会在启动时报 duplicate entry。切换通道前先删掉另一通道的行(bundle 通道 ↔ 手动行 + link 依赖)。包内 patch 中预留的双挂载守卫(disabled: !!js …)默认注释停用。

提示:client 半改动硬刷新即生效;host 半(src/index.ts / src/mention.ts)改动需 dsh web 重启(或经 profile 热通道重挂载该 entry)后才加载新 bundle。

VS Code 扩展

选中 / 文件发送命令与对话文件点击的轮询通道由扩展 dsh.selection-reference(源码在 extension/)提供,需装入 serve-web 实例。文件打开通道需要 ≥ 0.1.2(0.1.1 会在每次 workbench 重启时重放已消费的命令——见下方重放防护;其能力标记过不了版本探测,打开点击会安全降级为 URL payload 重载):

scripts/install-extension.sh                  # 打包 VSIX → 装入 serve-web → 注册清单 → 重启 → 健康检查
scripts/install-extension.sh --skip-build     # 复用已构建的 VSIX
scripts/install-extension.sh --vsix     # 使用指定 VSIX

本机的 code 是 standalone CLI(无桌面安装),code --install-extension 不可用,脚本以「vsce 打包 → 落文件 → 注册 extensions.json 清单 → 按原参数重启 serve-web」四步完成安装。分步流程与排障见 scripts/install-extension.md。

重放防护(≥ 0.1.2) —— 关闭侧边栏 VSCode 标签会把 workbench iframe 整个从 DOM 撕掉,重新打开时扩展宿主全新启动;一条在消费后仍留在 spool 里的命令会在每次这样的重启时重新打开它的文件(即「文件已关闭、下次启动 VS Code 又自动打开」bug)。三重独立防护:扩展消费即删除 cmd.json(垃圾/超期命令同样当场删除——超过 10 分钟的命令永不打开)、已消费 nonce 水位线持久化在 last.json(删除失败的兜底)、带版本的 cap.json 标记({v:2,at})让客户端能力探测直接拒绝会重放的 0.1.1。

内嵌启动干净开场:台账 + 对账 + 隐藏揭幕(≥ 0.1.2) —— iframe 的销毁同样跳过 VS Code 的 unload 生命周期,其编辑器状态恢复可能重放用户在关标签前几秒刚关闭的文件(VS Code 仅周期性落盘工作区编辑器状态)。本模型精确复原关闭前仍打开的文件,且中间过程不可见:

- 编辑器台账(editors.json)——扩展在每次标签变动时把窗口当前打开的文件标签(顺序 + 活动编辑器)同步写进 spool,销毁竞态无法弄丢它;下次激活时磁盘上的内容就是上一会话的最终状态。
- 启动对账——激活时先等 VS Code 自身的恢复落定,再让窗口对齐台账:台账里没有的恢复标签(关标签前已关闭的文件)被关闭(脏标签保留,数据优先)、恢复丢失的台账文件被补开、活动编辑器复原。无台账的启动(工作区首次启动,或降级 URL-payload 打开)完全不动 VS Code 自身行为。
- 隐藏揭幕(boot.begin / boot.status)——挂载 iframe 之前,标签页先在 spool 里停放一枚启动 nonce(bootreq.json);扩展对账完成后在 boot.json 回执里回显它,客户端在此之前让 iframe 保持 opacity 0、以加载遮罩示人。用户看到的第一帧就是对账完的编辑器区——绝不会先看见某文件被打开又被关掉。揭幕是一场竞速而非单一等待:回执轮询与 DOM 静默观察器(同源读取 workbench 的编辑器标签条)并联,先到先赢——原地重载后的扩展宿主可能不再重新激活(回执永远不来),而已经画完的工作台本身就是「布景完成」的证明。但仅静默构不成这个证明:幽灵启动的整个对账 settle 窗口内,标签条是「静默但错误」的(静默恰恰是随后那次关闭的前置条件,绝不是关闭已发生的证据),因此竞速者是账本感知的——boot.begin 应答会一并带回停靠时启动账本的期望开档集合,静默揭幕(以及 4 秒轮询预算耗尽后的兜底)还额外要求采样到的标签条与该集合按基名多重集一致(标签条 DOM 不暴露全路径);回执匹配仍无条件揭幕,无账本(首次启动 / 较旧 host 半)照旧仅凭静默,而账本不吻合的标签条会让 iframe 一直藏到对账关闭落地、或观察器自身 8 秒上限兜底。提前揭幕还有第二重危险,仅靠握手看不见:用户在「已揭幕、对账未跑」窗口里打开的文件晚于对账所依据的账本,会被当幽灵关掉——因此揭幕后帧内的首个用户手势会落盘 interact.json(nonce 作用域,路由 boot.interact),对账的关闭循环与幽灵补刀都会为本 boot 的交互标记让步(回执报告让步数;陈旧标记绝不会解除后续 boot 的武装)。全链路 fail-soft:较旧的 host 半(boot 路由尚未重载)由 DOM 静默观察器单独裁决,跨源直连 iframe 则不加门控、按原生行为可见启动。
- 跨标签启动锁——两个同源 DSH 页面并发启动工作台时会竞争创建 VS Code 的 IndexedDB 存储(vscode-web-db),败者可能在自身启动过程中挂死数分钟(实测:一次 294 秒的库打开;两个全新 profile 各自卡到另一标签页关闭为止)。因此 iframe 挂载前必须先持有 Web Lock dsh-sidebar-vscode:workbench-boot,工作台绘制完成即释放(那一刻创建竞争已经结束);排队超过半秒会在加载遮罩中说明缘由。设计上处处 fail-open:没有 Web Locks API、锁被卡死的持有者(60 秒等待上限)、或不可读的框架(30 秒持有上限)都选择放行而不是阻塞。
- 台账归属围栏 + 启动轮换(扩展 ≥ 0.1.3)——serve-web 在渲染器消失后也会让扩展宿主存活一段时间,而这类残留宿主的台账处理器仍然 armed:它会把自己那个不可见窗口的标签集写进共享的 editors.json,对账的 reopen 循环还会把台账文件开进该窗口、其标签事件又反过来重写台账——台账就此被可见 workbench 从未展示过的文件毒化,之后每次启动都如实复活(「已关闭的文件又回来了」)。台账现在归属本次启动:每次台账写入、对账本身、以及幽灵补刀都会复核停放的启动 nonce 是否仍是本宿主激活时的那枚。客户端在 iframe 原地重载时(工作区 / serverUrl / pathMap 变化、降级通道 payload、手动刷新——运行时的换键路径;新渲染器的宿主若沿用旧 nonce 就会错配。单纯切换标签已不再触发任何重载,见「常驻工作台」一节)以及工作台销毁时轮换该 nonce,把所有仍持旧 nonce 的宿主一并退役。
- 迟到幽灵补刀(扩展 ≥ 0.1.3)——VS Code 自身的恢复可能在 对账的 settle 预算耗尽之后还在陆续落标签(重工作区的慢启动),而这些迟到者恰恰是没人会再去关的「已关闭文件幽灵」。武装之后会间隔着跑几轮只关不开的差分:每轮关掉台账未列出且处于后台的标签,豁免脏标签与活动编辑器(重新挂载后数秒内,用户主动打开的文件必然成为活动编辑器,而恢复幽灵落在后台);任何一轮都不会打开文件。

使用方法

打开标签页

展开右侧边栏,在引导页(「开始」页)选择 VSCode 工作台 入口框——它在引导标签的位置打开。布局仅内存态:刷新页面后每个会话回到折叠默认态。工具栏显示当前工作区路径,「⧉ 在新窗口打开」可弹出独立窗口。

发送选区

1. 在嵌入的编辑器里选中代码(多光标 = 多条 chip);
2. 右键菜单「DSH: 发送选中代码到会话」,或按 Ctrl/Cmd+Alt+C;
3. 输入框出现原子 chip @src/main.ts L10-L12——注入成功静默无提示,chip 出现即反馈;仅降级 / 失败时工具栏闪琥珀色提示;
4. 照常输入并提交。chip 被改写为可读 @path L10-L12, context 紧跟该消息注入。

选区引用行为细则:

- 去重:同一 step 内按 (路径, 起始行, 结束行) 去重——重复发送同一选区只注入一条 context;同范围不同内容(文件已改)保留最新捕获;
- 时效:提交时在会话 cwd 约束下重读磁盘行区间比对哈希——不一致标 stale="true";截断快照改为校验保留的首尾两半(截断本身绝不导致 stale);未保存缓冲区标 dirty="true";快照内容始终注入(不依赖文件系统),tag 首部注释提示模型修改前先重新读取;
- 截断:超过 maxLines(默认 200)/ maxBytes(默认 20000,防单行压缩大文件)时保留首尾两半、省略中间,正文内嵌 ... (N lines omitted, L51-L150) ... 标记,标签带 truncated="true";行号保留真实范围;
- 注入的 context 消息 source 为 { kind: 'vscode-mention', form: 'notice', version: 1, path, startLine, endLine, language?, contentHash, bytes, truncated, dirty, stale }。

发送文件 / 文件夹

资源管理器选中文件 / 文件夹(可多选混选),右键「发送文件 / 发送文件夹到会话」(命令位于「复制路径」附近)。每项一条 chip:文件 @src/main.ts(文件图标)、文件夹 @src(文件夹图标)。类型由扩展侧 workspace.fs.stat 判定(symlink 按目标归类)。提交时展开为  / ,source 为 { kind: 'vscode-resource', form: 'notice', version: 1, path, type }。资源引用不做新鲜度检查、不受截断上限约束;同一 step 内按 (路径, 类型) 去重,同一路径的选区引用与资源引用互为独立引用。

管理引用

- 输入框上方 tag 栏归并显示全部 VS Code 引用(截断徽标 …、文件夹图标、出现次数 ×N);点 tag 的 × 一次移除该引用的全部 chip;
- chip 本身退格一次删整条;草稿里该引用的 mention 全部消失后,提交即不再注入;
- 复制 chip(在对话里渲染后)再粘回输入框,会重建为原子 chip。

设置

设置位于官方插件配置卡片——设置 → 插件 → 插件配置 → VSCode 侧边栏:宿主半注册 vscode-sidebar 设置命名空间(ctx.settings.installSection),浏览器半注册按其键位的 settings.plugin.item 卡片,每行经 ctx.settingsScope 提交(set/unset;卡片头部的「恢复默认」逐字段清除用户层,各自回到组合基线)。不在 cordis.patch.yml(下表的 pathMap 由同一命名空间伺服,但刻意不设卡片行):

| 键 | 默认 | 说明 |
|---|---|---|
| openAsDefault | false | 控制三个文件打开接管(对话文件点击、设置页「打开配置文件」、右侧边栏展开按钮)。关闭后处处恢复官方默认行为 |
| openBlocklist | (未设 = pdf, docx, xlsx, pptx, png, jpeg, jpg) | 「不由 VSCode 打开的文件类型」:后缀字符串数组,面板行为 tag 编辑器(tag 带 × 删除;输入框自由输入后缀,回车/逗号添加,下拉建议常用二进制类型)。命中后缀的文件在对话中点击时不被接管——原样落回官方路由,由官方侧栏的查看器认领(今天是内置文本预览;未来查看器插件会注册自己的官方类型),绝不落宿主 OS 打开器。未设置 = 默认七项;清空 = 存 [],即全部由 VSCode 打开(两者语义不同)。匹配大小写不敏感,按「基名以 .后缀 结尾」判定(a.notpdf 不命中 pdf;条目可含内部点,如 tar.gz;无后缀文件永不命中);每次点击时现读设置,改动即时生效。设置页「打开配置文件」接管不受此表影响 |
| serverUrl | (空 = http://127.0.0.1:8000) | code serve-web 输出的完整地址(可含基路径与 ?tkn= 令牌);留空 = 默认 http://127.0.0.1:8000(本机裸启动)。代理可达时一律挂载为同源 /sidebar/vscode/ 打开;宿主不可达时完整地址回退直连(同源桥降级);显式相对子路径(如 /vscode)在代理关闭时按网关语义使用 |
| pathMap | (空 = 不映射) | 仅配置文件——无设置面板行(少见,分容器部署才需要)。DSH 路径前缀 → VSCode 容器路径前缀,源=目标 对用 ; 分隔;最长源前缀优先;某前缀已是映射目标时原样透传。留空时不做任何映射,会话 cwd 与文件均按原始绝对路径直接打开(同容器部署即用此默认)。规则只做前缀改写、不是白名单:未命中任何规则的绝对路径原样透传(文件真不存在时由 VS Code 报错兜底) |
| maxLines | 200(范围 1–2000) | 单次引用注入的代码行数上限,超出保留首尾两半、省略中间并标注省略区间 |
| maxBytes | 20000(范围 1000–200000) | 单次引用注入的 UTF-8 字节上限(防压缩成一行的超大文件) |

(选中代码注入功能常开,无开关。)数值行在输入时即校验范围(越界红框 + 行内提示,确认时吸附到最近边界);文本行为上下布局(说明在上、输入框独占一行);openBlocklist 行为 tag + 内联输入(非法后缀红框提示,重复添加为静默无操作)。

排障

| 症状 | 处理 |
|---|---|
| 标签页长时间空白 / 加载提示不消失 | 检查「功能设置」里的 serverUrl 是否可达;用「在新窗口打开」直连排查;跨域地址下同源桥不可用属预期(粘贴兜底仍可用)。用内置反代时确认 serve-web 已在配置的上游应答(默认 http://127.0.0.1:8000,任意基路径均可) |
| 命中 openBlocklist 的文件改在自带「文件」标签打开(或点击报错) | 黑名单命中 = 改道侧边栏自带「文件」标签(其查看器负责渲染该类型);仅当「文件」标签类型在侧边卡片设置里被禁用时才回落宿主 OS 打开器,无头容器上该打开器可能缺失。若希望该类型仍由 VSCode 打开,从列表移除对应后缀即可 |
| 工具栏提示「当前工作区路径不是绝对路径…」 | 仅在会话 cwd 不是绝对路径时出现(workbench 已按默认界面打开);正常部署不会触发,无需配置映射 |
| 右键没有 DSH 命令 / 命令面板搜不到 | 扩展未装或 serve-web 未重启(清单仅启动时扫描),或工作区处于受限模式未信任——见 scripts/install-extension.md 常见问题表 |
| 发送后 chip 未出现,剪贴板出现代码片段 | 注入降级为可读回退文本(无可用输入框 / 跨域);直接粘贴进输入框即可恢复为 chip |
| 提示「已注入为文本引用…」 | 输入框处于提交中等瞬态,chip 化被拒,已退化为纯文本 mention——提交效果相同 |
| 新建会话后光标闪两下就消失,或从其他工作区会话切回后焦点跳进侧边栏打开的文件里(旧版本症状) | workbench 开机会聚焦自身(欢迎页渲染即抢、工作区恢复时聚焦恢复的编辑器):收起面板后的隐形启动会无声夺走光标;跨工作区切回触发的重插重载又恰好抢在输入框自动聚焦之后。现版本通用防护——首次加载延迟到标签真正可见,且帧的每次开机都带焦点围栏,把未经邀请的抢焦点弹回原处,用户真实点进 workbench 则立即放行(见下文「焦点防护」);展开侧边栏时的首次加载会稍晚出现工作台,属预期 |

技术架构

双端结构

DSH 插件分 host(node)半与 browser 半,本插件各自职责:

┌─ 共享协议平面 ────────────────────────────────────────────────┐
│ src/shared/protocol.ts  跨进程边界的全部常量,单一事实来源;      │
│                         扩展侧 CJS 镜像由锁步测试钉死不漂移      │
└───────────────────────────────────────────────────────────────┘
┌─ host 半 (node) ──────────────────────────────────────────────┐
│ src/index.ts    agent/created → 在每个 agent 作用域挂 pre-step │
│ src/mention.ts  引用边界核心:解析改写 / 去重 / 新鲜度 / 注入    │
└───────────────────────────────────────────────────────────────┘
┌─ browser 半 (web) ────────────────────────────────────────────┐
│ src/client/index.tsx        注册 tab + dock + @ 触发源 + 词典   │
│ src/client/VscodeView.tsx   标签视图:投影锚点 + 外壳            │
│ src/client/Controller      启动门控/焦点围栏/基址/打开请求/打开 │
│                              编排器(均可注入依赖单测)          │
│ src/client/references.ts    载荷→chip、插入、tag 栏、粘贴恢复    │
│ src/client/composer.tsx     dock 组件:引用 tag 栏 + 粘贴兜底    │
│ …(完整清单见下文目录结构)                                       │
└───────────────────────────────────────────────────────────────┘

- host 半是模型 facing 边界:对每个存活 agent 在 agent/pre-step 监听,解析被认领用户消息中的规范 mention(markdown 与裸 URI,两种 scheme,严格 canonical 校验),改写为可读标签(freezeMessage 保留消息 id),按引用身份去重后逐条注入 context(createUserMessage,紧跟首次引用它的消息)。文件系统只用于新鲜度标记——快照内容随 mention 携带,注入不依赖磁盘状态;
- browser 半负责全部 UI:官方 vscode 标签类型与正文、chip、tag 栏、设置卡片、接管、词典;官方侧栏服务缺席时客户端 fiber 静默等待、什么都不注册。

常驻工作台(切换标签为何零成本)

官方 pane 只渲染活动标签的正文,而 iframe 元素一旦离开父节点,其嵌套浏览上下文即被销毁——HTML 规范的 removing steps 会 destroy child navigable,且 Chromium 151 实测:即使同 document 内移动 iframe(或移动其祖先容器)也会整帧重载。VS Code 没有「快照重建」路径(ui-sidebar-terminal 用控制器里的屏幕修订重建 xterm——本设计借用的模式,换成唯一一种「状态即文档」的元素),因此工作台要活过标签切换,只有一条路:元素永不离开 DOM。

- workbenchRuntime.ts 持有页面级单例——一个一次性挂到 document.body 的宿主 div,iframe 在首次获准加载时创建于其中,销毁时才移除。全部启动门控控制器(boot lock、boot gate、焦点围栏、打开通道 opener、剪贴板桥)都住进运行时,状态因此活过标签正文的挂载周期。视图(VscodeView.tsx)只渲染占位符与外壳,向运行时喂解析后的输入,并按 ${sessionId}:${tabId} 采纳它;
- projection.ts 把宿主盒子粘在占位矩形上——可见期间每动画帧一次 getBoundingClientRect 读 + 一次样式写,投影帧因此能跟上面板滑动、拖宽手柄、全屏切换与浮窗拖动而无需移动 iframe。层叠策略:停靠 / 全屏 45(高于全屏面板的 40、低于浮窗宿主的 60),占位符浮出时 61(高于浮层、低于 70 的菜单)。隐藏时以 visibility 保留最后矩形——宿主归零会逼 VS Code 在每次隐藏/显示周期里重排布局;
- 防泄漏纪律:每页最多一个活工作台(basis 变化——换工作区、改设置——原地重载,绝不出现第二个实例;boot lock 存在的意义正是防止两个同源并发启动把 VS Code 的 IndexedDB 死锁);运行活过正文卸载,在最后一个采纳标签记录消失时销毁(框架在记录移除时 abort 的 tab signal),插件卸载时也销毁(client 入口 teardown 里的 destroyWorkbenchRuntime())。两个 pane 可同时持有同 kind——最后挂载者拥有投影,其释放时回退到前一个 pane 的占位符而非留白。

对旧防护所针对的重载路径的影响:同 pane 切标签、面板收起/展开、同工作区切换会话,现在都不再重载(帧在后台继续运行——连启动都会在后台完成);原地重载只剩:工作区 / serverUrl / pathMap 变化、降级通道 payload、手动刷新按钮——恰好是启动门 nonce 轮换与账本对账仍然发挥作用的场合。重挂载后的瞬时 base 重解析也绝不拆掉活帧——运行时持有上一个已解析 base,直到出现不同的解析结果。

四级链路

选区与资源引用共用一条链路:

1. VS Code 扩展(extension/):选区命令打包 { path, relative?, language?, dirty?, spans[] };资源命令对每个 URI workspace.fs.stat 判型打包 { kind: 'resource', resources: [{ path, relative?, type }] }(无内容),经 vscode.env.clipboard.writeText 写入信封 @@DSH_REF::::\n;
2. 剪贴板信号桥(src/client/clipboardBridge.ts):同源 iframe 特权——父页面在 workbench window.navigator.clipboard.writeText 上打补丁,拦截扩展宿主的剪贴板写入链(ext host → MainThreadClipboard → BrowserClipboardService → 晚绑定的 navigator.clipboard.writeText);注入成功时完全不触碰真实剪贴板,仅失败时才把可读回退写入供手动粘贴;跨域 URL 下桥直接 no-op;
3. composer chip(src/client/references.ts + composer.tsx):载荷经 pathMap 反向映射回 DSH 路径(cwd 之下相对化)、截断(首尾两半)、crypto.subtle 计算 sha-256 前缀,编成规范 mention,经 conversation.input 服务的 insertReference 落为原子 occurrence chip——落点是输入框当前光标:优先经输入解析器的 keyboard face(caretSpan(),Lexical 时代 composer 自身的光标投影)取目标会话的实时选区,退回显示中 composer 的 DOM 选区经 detect 投影映射(composerDom.ts——chip 在其中只算一个原子字符);只要该表面属于目标会话即可用(有选区则替换、多条 chip 按序连排);光标不可得(会话不匹配 / 无可用输入框)时保持历史的草稿末尾零宽 span CAS。span 坐标在 Lexical 宿主(DSH ≥ 0.1.2-alpha.2:草稿是 clipboard 投影,chip 在其中展开为完整 clipboardText,而在 span 坐标里只是一个 )取 detect 投影,在旧 textarea 时代机器取草稿坐标——一个结构探测(输入门面上的 editor)选定坐标平面;纯文本降级走会话级 'slash/input-insert-text' 事件,草稿里其余 chip 在落地后完好无损;本插件注册名为 vscode-reference 的 @ 触发源(候选恒空,仅为提交序列化路由 codec);conversation.input.dock 组件渲染引用 tag 栏(关闭按钮经会话级 'slash/input-consume-token' 事件移除该引用的全部 chip——Lexical 宿主下保 chip:整稿 setDraft 写入会把其余 chip 全部压成裸 mention 文本)并在 document 捕获相拦截粘贴,同时适配 contenteditable composer 与旧 textarea(信封走注入 lander 并落在粘贴光标处;mention 复制体经解析后落 chip——preventDefault 之外还要 stopPropagation,仅 preventDefault 拦不住 composer 自身的粘贴处理);
4. host 边界(src/mention.ts):严格解析之外再追加一层 fail-soft 恢复扫描兜住散架复制体;闭合标签碰撞用内容哈希盐化防伪造。

mention 编解码

- 规范形态:@[](dsh-vscode:)(选区)/ dsh-vscode-res:(资源);载荷自包含(路径 / 行号 / 快照 / 哈希 / 标志),草稿文本即唯一存储;两个 scheme 前缀互斥,互不误配;
- 解码必须重新编码为完全相同的 URI(canonical 纪律,与 dsh-session: 引用一致):显式 markdown mention 遇到畸形 URI 严格报错;裸文本仅在 base64url 形态跟随 scheme 时才算引用,且仍须通过 canonical 校验;
- 恢复层(scanRecoveredMentions)识别 sigil 被空白撑开的复制体与丢失闭合括号的截断复制体;投影只从完整通过校验的载荷重建——复制文本里的 label 一律不信任(展示残留),全部由载荷重新推导;
- 共享纯逻辑模块 src/mentionCodec.ts 无 Node 内建、无 @deepseek-ai/ 值导入,host 与 browser 两个 bundle 原样复用(client 纯度门通过)。

截断与新鲜度

- 捕获时(truncateSnapshot):LF 归一化 → 行数上限(保留首尾两半整行)→ 字节上限(首段从尾部缩、尾段从头部缩,多字节安全);载荷记录 headLen / omitLines / omitBytes,host 渲染内嵌省略标记,标记本身不计入计数;
- 提交时(freshnessOf):在会话 cwd 约束下重读磁盘行区间(路径越界 / 文件超 8 MiB / 读取失败一律 unknown),哈希比对得 fresh / stale;截断快照校验磁盘区间以保留首段开头、以保留尾段结尾且中间至少一字符(被省略中间的改动不可检测,截断本身不致 stale)。

打开导航(单发打开命令)

官方侧栏把一次打开送到标签正文的形式是 tab.navigation —— { address, params, revision },每次导航 revision 递增——这正是 better-sidebar 时代要靠 openRequest meta + 墙钟 nonce + 持久化 meta 卫生手工搭建的「单发命令载体」。src/client/openRequests.ts 只保留仍然要紧的纪律:revision 0(种入的引导、撤销恢复的记录)不是任何人的点击;页级水位表(${sessionId}:${tabId} → 已执行的最高 revision)让重挂载跳过已执行过的导航、而挂载批次的点击仍然执行;帧的启动门未落定时新导航延迟执行(其打开命令须携带启动 nonce)。navigation 消费者在执行时铸造扩展命令 nonce,把打开交给双通道 opener(workbenchLink.ts)。
焦点防护(workbench 必须自己赢得焦点)

VS Code workbench 开机后会编程性聚焦自身内容——Getting Started 欢迎页渲染即 focus() 自身,恢复的工作区会聚焦它恢复的编辑器——大约在 iframe 加载后 0.5–4 秒发生,与用户是否交互无关。只要这次开机落在用户并非奔着 workbench 去的时刻,光标就会被从脚下夺走:新会话的默认标签在收起面板后隐形启动会无声夺走光标(闪两下);而跨工作区切换会话时 workbench 会原地重载(basis 变了;同工作区切换则原样复用活帧、零重载)——恢复的文件恰好抢在输入框自动聚焦之后夺走焦点。src/client/VscodeView.tsx + workbenchRuntime.ts 用两个机制防护:

- 首次加载延迟:iframe 一直挂起,直到本标签真正可见过至少一次(官方停靠正文可见性:活动标签且面板展开;浮窗恒可见)。接管打开若落在面板收起时,workbench 等待观众——隐藏启动用户什么都看不到,还白抢焦点;延迟到首次揭示后再加载,启动期的任何焦点抓取都发生在用户正看着 workbench 的时候。延迟条件是运行时的 visibleOnce 门,因此活过标签正文的卸载周期——而一旦帧已存在,切到同 pane 其他标签就再无任何成本(正文卸载、帧转入后台运行,切回即时重新投影)。
- 焦点围栏(src/client/focusGuard.ts):焦点一进入帧内,立即还给帧外最近持有它的元素——归还次数按滑动窗口限额(默认 10 秒 5 次),病态循环抢焦点时围栏让位以免焦点乒乓——仅在两种情况下武装:
- 隐藏(显式 visible === false):帧收不到用户点击,任何进入都是偷取。面板收起或标签非当前时,抢焦点无从合法化。
- 开机:帧每次加载后的一个窗口期(默认 6 秒),因为每次加载都是一次 workbench 开机、每次开机都会自聚焦——页面刷新与工作区切回的重插都算。窗口期内进入会被弹回,除非用户在帧内做了手势(同源 pointerdown/keydown 追踪,每次加载重挂——刚开机的 workbench 一点即入),或父页面 Tab 键把焦点交了过来。唯一被认可的开机是上面的延迟首载:那是用户展开标签释放的加载,抢焦点正中下怀。跨域 iframe(直连回退模式)看不到帧内手势,开机围栏直接解除,绝不弹掉用户的真实点击。
- 识别走 focusout 而非 focusin(焦点穿入 iframe 时父文档只发 focusout,focusin 落在帧内文档)。

部署拓扑(默认值的依据)

VS Code server(code serve-web)直接跑在 dsh-runtime 容器里:

code serve-web --host 0.0.0.0 --port 8000 --server-base-path /vscode \
--server-data-dir /data/workspace/.vscode --without-connection-token \
--default-folder /data/workspace

nginx: location /vscode/ → 127.0.0.1:8000(含 WebSocket upgrade)
网关只做用户 → 实例透传,/vscode 无特例,增删用户零同步

没有网关层的部署(Windows / 局域网直跑 dsh web)不需要自建 nginx:插件宿主半在 dsh web
自己监听的端口上注册同等的反代(src/vscodeProxy.ts,挂载点 /sidebar/vscode)。HTTP 前缀路由
透传且保留浏览器 Host(serve-web 因此把 remoteAuthority 烘焙成 DSH 端口,一切回程 URL 走同源);
WebSocket upgrade 以精确路径 /- 注册——浏览器 socket 工厂只连这一
路径,serve-web 的 handleUpgrade 不校验路径。路由基路径以首页 HTML 烘焙的 serverBasePath
为准(探测入口 URL 说了不算):非挂载点基路径额外注册恒等镜像,根路径上游注册 -
补片。首页探测最多跟随三次重定向并采纳最终 origin,因此上游即便挂在会重定向的反代后面(如强制
http→https 跳转)也能正确发现并转发。上游优先取 serverUrl 的完整地址(经 proxy.config 推送),
其次 DSH_SIDEBAR_VSCODE_UPSTREAM(默认 http://127.0.0.1:8000,off 可关)。

DSH 会话与嵌入 workbench 看到同一文件系统、同一路径,因此 pathMap 默认留空 = 不映射:会话 cwd 与对话点击的文件都按原始绝对路径直接交给 workbench,不做任何改写(此前默认是两条恒等规则 /data/workspace=/data/workspace;/opt=/opt,只覆盖这两个根,其余目录一律提示无法映射)。规则是前缀改写器而非白名单:即便配置了规则,未命中的路径仍原样透传(存在性由 VS Code 判定),不会再出现阻断性提示。若把 workbench 挪去别的容器 / 挂载,在设置文档里用 pathMap 配置真实的前缀改写即可(该键无设置面板行)。

目录结构

代码按领域分层 —— 一个所有运行时共用常量的共享协议平面、host 半的服务、browser 半的控制器 + 视图、按同样缝隙分解的扩展 —— 任何契约的改动都只落在一处:

src/shared/protocol.ts         # 协议平面:跨进程边界的全部常量(信封标记、代理挂载路径、spool 文件名、能力版本、TTL、工作区 slug)——纯模块,host+client 原样引用
src/index.ts                   # host 半入口:agent/created → pre-step 边界挂载 + /sidebar-vscode/api 围栏路由(一张方法表分发)+ vscode-sidebar 设置段注册(inject: agents, webServer, webRuntime;嵌套 settings)
src/settingsSection.ts         # vscode-sidebar 设置段:schema + installSection(5 测试)
src/shared/settings.ts         # 两半共享的设置模型:命名空间、类型、组合基线
src/vscodeProxy.ts             # host 半:/sidebar/vscode 同源反代(HTTP 透传 + WS upgrade 管道 + 路径/令牌改写 + configure 通道)(41 测试)
src/mention.ts                 # host 半核心:解析改写/去重/新鲜度/ 等注入(38 测试)
src/mentionCodec.ts            # 共享纯逻辑:两种 scheme 规范 URI 编解码/截断/哈希归一(42 测试)
src/openChannel.ts             # host 半:/tmp 命令通道 spool —— 全部持久化走唯一 SpoolStore(原子写 + 容错读)(13 测试)
src/trust-fence.ts             # host 半:本插件路由的浏览器信任围栏(回环/trustedHosts + 同源标记)
src/client/index.tsx           # browser 半入口:薄组合根(tab + dock + @ 触发源 + 接管装配),机制全部在下列模块
src/client/VscodeView.tsx      # 标签视图:投影锚点 + 外壳(工具栏/提示/加载遮罩),向常驻运行时喂输入
src/client/workbenchRuntime.ts # 工作台守护者:每页一个常驻宿主 + 帧,活过标签正文卸载;lock→gate→src 协调器;采纳者 signal 回收(13 测试)
src/client/projection.ts       # 矩形投影器:rAF 追踪常驻宿主对准标签正文占位符 + 层叠策略(10 测试)
src/client/bootGate.ts         # BootGateController:nonce 停靠 → 回执 × DOM 静默揭幕竞速 → 原地重载轮换;+ DOM 静默观察器(13+5 测试)
src/client/bootLock.ts         # WorkbenchBootLock:跨标签 Web Lock 串行化首次绘制(vscode-web-db 创建竞争)+ acquireWebLock(11 测试)
src/client/focusFence.ts       # FocusFenceController:每次 load 的手势追踪 + 文档监听,包着 focusGuard 的纯规则
src/client/focusGuard.ts       # 焦点围栏纯决策 + 滑动窗口归还限额(7 测试)
src/client/workbenchBase.ts    # WorkbenchBaseController + useWorkbenchBase:mount/直连解析、上游推送/重置、毕业轮询(6 测试)
src/client/openRequests.ts     # OpenRequestConsumer:单发导航纪律 —— revision 0 静默、页级水位、门控延迟(10 测试)
src/client/workbenchLink.ts    # createWorkbenchOpener:优先扩展 spool(cap v4 起带启动标签),降级 URL payload 重载(8 测试)
src/client/clipboardBridge.ts  # 同源 iframe navigator.clipboard.writeText 信号补丁(10 测试;跨域读取抛 SecurityError 时 no-op)
src/client/composer.tsx        # dock 组件:引用 tag 栏 + 粘贴兜底(样式走 styles.ts)
src/client/composerDom.ts      # Lexical composer DOM 的 detect 投影遍历(DOM 选区 ⇄ detect 偏移映射)(11 测试)
src/client/references.ts       # 载荷→chip(选区/资源)/光标处插入/tag 栏投影/粘贴恢复(69 测试)
src/client/referencePipeline.ts # 插件体、标签页、dock 三方共享的 lander/选项句柄表
src/client/selection.ts        # 剪贴板信封编解码(选区 + 资源两种 payload)(16 测试)
src/client/paths.ts            # pathMap 解析/映射/反向映射、URL 构建(34 测试)
src/client/settings.ts         # vscode-sidebar 设置读取(scope 快照 + 基线回退)+ 截断上限契约(默认/边界/提交助手)(18 测试)
src/client/settingsCard.tsx    # 官方插件配置卡片(settings.plugin.item):卡片外壳 + 恢复默认 + 开关行 + 黑名单 tag 行 + 文本行 + 数值行(样式走 styles.ts)
src/client/openBlocklist.ts    # 「不由 VSCode 打开」后缀表:默认值 / 归一化 / 基名后缀匹配(24 测试)
src/client/settingsTakeover.ts # 设置页「打开配置文件」接管:两个时代包装器共用一个决策核 + 设置弹框关闭(17 测试)
src/client/openIntercept.ts    # 官方 openResource 接管:本地 file 地址解析器 + wrapSidebarRightOpenResource(门/黑名单放行/params 翻译)(22 测试)
src/client/takeovers.ts        # 接管家族装配:一张门控接到两条缝(openResource 漏斗 + 两条设置漏斗)
src/client/openChannelApi.ts   # 打开通道 client 半:围栏 /sidebar-vscode/api 探测与命令(11 测试)
src/client/definition.ts       # 官方 tab 类型定义:kind/id/params 面 + 引导页入口框
src/client/styles.ts           # 样式注册表:全部注入 CSS 块 + 一个幂等 adopter
src/client/i18n.ts             # locale 服务挂接 + t()
src/client/locales.ts          # zh/en 词典
src/client/icons.tsx           # VS Code 标志 + 引用 chip 文件/文件夹/关闭图标(currentColor SVG)
extension/                     # VS Code 扩展 dsh.selection-reference,按同样缝隙分解:
├ extension.js                #   ~90 行激活根(命令 → 读账本 → 对账 → 武装 → 轮询 → 幽灵补刀)
├ lib/protocol.js             #   共享协议平面的 CJS 镜像 —— 由 tests/protocolLockstep.spec.ts 与 src/shared/protocol.ts 锁步钉死
├ lib/fsutil.js               #   原子标记写 + spool 目录
├ lib/envelope.js             #   三个发送命令 + 它们乘坐的剪贴板信封
├ lib/channel.js              #   spool 轮询:能力标记 + 一次性命令消费(TTL/nonce/启动标签三重防护)
├ lib/boot.js                 #   编辑器账本 + 启动对账 + 迟到幽灵补刀 + 拥有它们的 nonce 围栏
├ harness.js / package.json / package.nls.json / .vscodeignore / vsix/.vsix
scripts/install-extension.sh  # 扩展一键安装(vsce 打包 → 落文件 → 注册清单 → 重启 → 健康检查)
scripts/install-extension.md  # 安装分步文档 + 排障表
README.md / README.zh-CN.md   # 英文文档 / 本文档(中文)
screenshot.png                # 产品使用截图(见上方「界面截图」)
tests/.spec.ts               # vitest 单测,共 511 例 / 24 文件(如上括注分文件计数)
cordis.patch.yml               # bundle 通道的 host 半插入行(挂载声明)
tsdown.config.ts               # 双 bundle 构建(host ESM + client ModuleLoader 格式 + 纯度门)
vitest.config.ts               # 测试期 dsh-llm 别名(优先 harness 检出,回退已装包)
lib/                           # 构建产物(随仓库提交:link: 部署直接服务 lib/client.js)
.github/workflows/ci.yml       # CI:Node 22 & 24 上 typecheck / test / build / 包内容校验

构建产物交付:host 半为普通 ESM bundle(@deepseek-ai/dsh-llm 保持外部导入,由 DSH host loader 解析);browser 半为 window.__ModuleLoader__.load({ id, factory }) 注册格式(官方外部 client 插件交付格式),React / cordis 走 external,并带纯度门——拒绝 Node 内建与 @deepseek-ai/ 值导入。

开发相关

构建与测试

git clone https://github.com/chendefine/dsh-sidebar-vscode && cd dsh-sidebar-vscode
pnpm build        # tsc 声明 + tsdown 双 bundle → lib/
pnpm typecheck    # tsc --noEmit
pnpm test         # vitest run(511 例)

重建后硬刷新浏览器即可(link: 依赖 + 内容 rev 查询参数自动破缓存);host 半改动需重启 dsh web。

环境要点

- pnpm ≥ 11:pnpm 专属设置只从 pnpm-workspace.yaml 读取(.npmrc 中的同名键会被静默忽略)。本仓库在 pnpm-workspace.yaml 固定 autoInstallPeers: false(@deepseek-ai/ 内部包不在公网 registry)与 verifyDepsBeforeRun: false(node_modules + lockfile 为冻结基线,跳过 run 前预检),以及 allowBuilds.node-pty: false(仅类型引用,不运行其原生构建);
- 类型与运行时映射:@deepseek-ai/ 构建期包(dsh-llm、dsh-agent 及 dsh-llm 的运行时 peer)均为从 npm registry 解析的 devDependencies,普通 clone 与 CI 开箱即用;tsconfig paths 与 vitest alias 在存在相邻 harness 检出(/app/dsh)时优先使用它(其构建产物比已发布 rc 更新),否则回退到已安装的包;
- CI:GitHub Actions(.github/workflows/ci.yml)在 Node 22 与 24 上运行 typecheck / test / build / 包内容校验——矩阵对齐 DSH 自身的支持范围(^22.19.0 || >=24.0.0,与发布的 engines 字段一致);
- devDependencies 基线:各 @deepseek-ai/ devDependencies(外加宿主侧设置 schema 用的 @deepseek-ai/schemastery)仅为类型、测试与开发期对齐——运行时它们都是可选 peer,由 DSH 宿主解析;
- 扩展手工测试:node extension/harness.js extension/extension.js(stub 掉注入的 vscode 模块,跑三条命令并打印信封与解码载荷)。

发布

npm 包名为 dsh-sidebar-vscode(仓库:chendefine/dsh-sidebar-vscode):

1. 提升 package.json 版本(extension/ 有改动时同步提升 extension/package.json)
2. 构建 + 测试,然后发布(prepublishOnly 会再跑一次构建)
pnpm test && pnpm publish --access public
3. 打 tag 并推送发布
git tag v && git push origin main --tags

extension/(VS Code 扩展)虽随 npm 包一起分发,但 DSH 本身从不加载它——它经 scripts/install-extension.sh 装入 serve-web 实例(见安装方法)。改动它之后,提升 extension/package.json 的版本并重跑脚本,保持随仓库提交的 VSIX 同步。

已知限制

- 宿主反代不可达时的直连回退形态下,同源剪贴板桥不可用(浏览器同源限制),仅剩粘贴兜底;若只是启动时序问题(serve-web 比 dsh web 晚就绪),客户端每 5s 轮询 proxy.status 的 serving,代理就绪后自动切回挂载点,无需手动刷新;
- 内置反代面向单上游(最后写入的 serverUrl 生效——多会话推送不同地址时全局共享一个);上游须 http(s) 直达(自签 TLS 需系统信任,URL 内嵌凭证不支持),代理不做鉴权剥离,令牌按原样附加。挂载路径继承 dsh web 端口的暴露面:能访问该端口的客户端即可使用被代理的工作台(?tkn= 令牌只保护上游、不保护挂载——代理会透明追加),请让端口与 GUI 本体处于同一信任边界(回环/受信网关);
- 选中注入常开,无开关;
- host 半代码改动需 dsh web 重启后生效;
- 构建时 tsdown 对 external / noExternal 报弃用警告(构建产物正确,迁移到 deps. 待后续)。

许可证

MIT(见 LICENSE)。

上游仓库有新提交时邮件通知你(每天最多一封,无更新不打扰),随时一键退订。

💬 加入 DPharness 群聊

插件用法、部署报错、新插件第一时间同步——群里问,比一个人翻文档快。

点击加入 QQ 群
DPharness 群聊二维码,手机 QQ 扫码进群
扫码进群