DeepSeek Harness 桌面端的原生 AI 代码工作台插件,提供文件树、Monaco 编辑器、Tab AI…
31.4/100
综合分
2
GitHub 星标
270
npm 周下载
—/5
用户评分
数据截至 2026/10/31 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add dsh-code-workbenchnpm 源 · dsh-code-workbench 的发布者账号已校验与本仓库一致
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · tool
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 5 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过安装清单取自 dsh-code-workbench/package.json
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✓npm 包dsh-code-workbench @ 0.5.4
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/11 22:14:28
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
读写/删除本地文件发起外部网络请求
查看扫描证据(共 24 处,展示前 24 条)
- 写入本地文件index.js:1154
await disk.mkdir(target.displayPath); - 删除本地文件index.js:1158
await disk.rm(target.displayPath, { recursive: true }); - 读取本地文件index.js:1170
const taken = await disk.lstat(destination.displayPath).then(() => true, (error) => { - 写入本地文件index.js:1178
await disk.rename(target.displayPath, destination.displayPath); - 发起网络请求index.js:1297
const response = await fetch(url, { - 读取本地文件index.js:1307
const manifest = JSON.parse(await disk.readFile(join(INSTALL_DIR, 'package.json'), 'utf8')); - 读取本地文件index.js:1393
return createHash('sha256').update(normalisedLineEndings(await disk.readFile(path))).digest('hex'); - 写入本地文件index.js:1450
await disk.mkdir(dirname(target), { recursive: true }); - 写入本地文件index.js:1452
await disk.writeFile(staging, bytes); - 写入本地文件index.js:1454
await disk.rename(staging, target); - 删除本地文件index.js:1457
await disk.rm(staging, { force: true }); - 发起网络请求completion-api.mjs:157
const response = await fetch(fimEndpoint(config.baseUrl), { - 发起网络请求completion-api.mts:211
const response = await fetch(fimEndpoint(config.baseUrl), { - 写入本地文件index.ts:1238
await disk.mkdir(target.displayPath) - 删除本地文件index.ts:1242
await disk.rm(target.displayPath, { recursive: true }) - 读取本地文件index.ts:1254
const taken = await disk.lstat(destination.displayPath).then(() => true, (error: unknown) => { - 写入本地文件index.ts:1260
if (operation === 'rename') await disk.rename(target.displayPath, destination.displayPath) - 发起网络请求index.ts:1397
const response = await fetch(url, { - 读取本地文件index.ts:1414
const manifest = JSON.parse(await disk.readFile(join(INSTALL_DIR, 'package.json'), 'utf8')) as { version?: unknown } - 读取本地文件index.ts:1498
return createHash('sha256').update(normalisedLineEndings(await disk.readFile(path))).digest('hex') - 写入本地文件index.ts:1553
await disk.mkdir(dirname(target), { recursive: true }) - 写入本地文件index.ts:1555
await disk.writeFile(staging, bytes) - 写入本地文件index.ts:1557
await disk.rename(staging, target) - 删除本地文件index.ts:1559
await disk.rm(staging, { force: true })
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 文件树浏览
- Monaco编辑器
- TabAI补全
README
由 DeepSeek 最新模型翻译生成DSH Code Workbench
简体中文 · English · 繁體中文
给 DSH Web GUI 的右侧栏加一个 VS Code 风格的代码工作台:用 Monaco(VS Code 编辑器内核本体) 浏览和编辑工作区文件——语法高亮、多光标、查找、撤销,全部与 VS Code 同款。在此之上还有代码库检索、Tab 补全、保存检查点与回滚、以及把代码加进对话。
设计原则:只组合官方机制,不自建平行协议。 写盘走官方 Connection 通道 + ctx.fs,读盘走官方 workspaceFiles Remote,预览交给官方文档预览,聊天引用走官方 chip 通道。插件自己不发明任何协议、不复制任何注册表。
安装
在 DSH 侧边栏的插件页点添加插件,把下面任一个填进对话框,装完点立即启用。已安装但没启用的组合包,之后也能在插件页开启。
展开完整文档(README 剩余 1 节)
DSH Code Workbench
简体中文 · English · 繁體中文
给 DSH Web GUI 的右侧栏加一个 VS Code 风格的代码工作台:用 Monaco(VS Code 编辑器内核本体) 浏览和编辑工作区文件——语法高亮、多光标、查找、撤销,全部与 VS Code 同款。在此之上还有代码库检索、Tab 补全、保存检查点与回滚、以及把代码加进对话。
设计原则:只组合官方机制,不自建平行协议。 写盘走官方 Connection 通道 + ctx.fs,读盘走官方 workspaceFiles Remote,预览交给官方文档预览,聊天引用走官方 chip 通道。插件自己不发明任何协议、不复制任何注册表。
安装
在 DSH 侧边栏的插件页点添加插件,把下面任一个填进对话框,装完点立即启用。已安装但没启用的组合包,之后也能在插件页开启。
从 npm 安装(最省事)
dsh-code-workbench
需要固定版本时带上版本号:
dsh-code-workbench@0.6.4
从 GitHub 安装
https://github.com/FinalLawer/dsh-native-code-workbench#path:/dsh-code-workbench
固定版本请把 tag 一起带上:
https://github.com/FinalLawer/dsh-native-code-workbench#v0.6.4&path:/dsh-code-workbench
⚠️ #path:/dsh-code-workbench 这一段不能省。 这个仓库的根目录不是包——插件在 dsh-code-workbench/ 子目录里,根目录没有 package.json。省略它时 pnpm 仍会报告安装成功,但装出来的是一个名为 dsh-native-code-workbench.git、版本 0.0.0 的占位包(清单是 pnpm 生成的 {"_pnpmPlaceholder": …}),既没有包清单也没有 dsh.bundle 声明,DSH 读不到组合包,插件不会出现。这个失败发生在安装之后,光看安装过程看不出来。走 npm 或压缩包则没有这个问题。
从压缩包安装
把 dsh-code-workbench-<版本>.tgz 放到任意位置,在同一个对话框里填它的绝对路径。三条路径装进 profile 的内容一致(按 files 白名单,20 个文件)。
依赖与升级
安装需要目标机器有 pnpm;走 GitHub 链接还需要 git,npm 和压缩包不需要。包里的 client.js 是预构建产物,安装过程不执行任何构建脚本,所以不会触发 pnpm 的依赖脚本拦截。
升级:官方安装器只提供安装与移除,没有 update 子命令;插件自带「检查更新 / 一键升级」(见下文「当前能力」)。手动升级也可以把新链接直接粘进安装框回车——对同名插件的拦截只比对裸包名,带完整 URL 不会被拦。对外分发建议带 tag 或版本号,而不是跟最新 master。
工作台布局
文件树上方的横排切换文件、搜索和历史视图(居中),再次点击当前视图把导航收起到只剩这一行。文件树自己的工具——新建文件、新建文件夹、刷新、全部折叠——位于工作区根节点那一行的右端,鼠标进入文件树时才显示。编辑区显示多文件标签(每个标签带文件类型图标),打开的那个标签与编辑器连成一片(顶部无黑线、下方无分隔线),配色与控件跟随 DSH 主题。
文件树里单击空白处会清空选中;顶部的新建按钮于是回到「在工作区根目录创建」,选中某个目录时则改为在该目录中创建。文件树只给选中画标记,当前打开的文件在树里不带任何标记——哪个文件开着由标签条回答(恰好一个活动标签)。编辑区的标签可以拖动:在标签条内左右拖可以换位,拖进主对话输入框则落成一枚引用胶囊(与拖文件树行同形)。
每个打开的文件保留独立编辑内容、撤销记录和光标/滚动位置,切换标签不丢失未保存修改。标签上的圆点表示未保存;关闭时可确认放弃或取消后保存。开启自动保存后,切换文件也会保存原文件。重命名、移动或删除前必须先保存受影响的打开文件;标签随后同步更新。刷新文件树会重新加载已展开目录。
工作台只渲染 UTF-8 文本,所以 PDF、Excel/CSV、Word/PPT、图片这类文件在这里没有可显示的内容:点击它们不会开标签页,而是把文件地址交给右栏的 DSH 官方文档预览打开(状态栏会说明文件去了哪里),右栏因此多出一个预览标签,可与「代码工作台」并排。若当前环境没有注册能认领该地址的预览实现,工作台退回自己的错误提示并注明该去哪看,不会假装打开过。
文件树右键菜单
右键点击文件、目录或文件树空白处,可以新建文件和文件夹、在资源管理器中显示、添加路径引用到当前对话、打开工作区终端、剪切/复制/粘贴、复制路径、重命名及删除。新建和重命名使用工作台内的名称弹窗,删除使用确认弹窗。剪切和复制后,在目标文件夹右键选择粘贴;复制遇到同名文件会自动生成副本名称。「添加路径引用」或把文件树行拖进输入框会落成一枚引用胶囊,发送时展开为与官方 @ 引用同形的 @路径 mention(目录带尾斜杠):聊天记录里因此渲染回同一个胶囊、点按在右栏预览,Agent 则按系统提示里那条 @ 指引先读再下结论。文件操作要求会话允许工作区写入,禁止覆盖已有目标和修改工作区外路径;删除需要确认且不能撤销。
选中只由用户放下:单击(左键)文件树的空白处会清空选中。落在行上、行内控件、工具栏(包括工具栏自己的空白)或输入框里的点击不算空白——否则「选中一行」和「清空选中」会重合成一个手势,而选中目录后往右上走点「新建文件」的擦边点击会先把目标打回根目录。清空后顶部新建按钮回到在工作区根目录创建;当前打开的文件不受影响(选中是「操作目标」,不是「正在看的文件」)。
「在文件夹中查找」仍然没有。菜单不包含运行测试、调试测试、运行覆盖率测试,目前未加入「添加到新对话」。
「打开工作区终端」这个入口曾经被移除(当时没有可用的官方通道),现在回来了。它调用右栏导航控制器的 openTab('terminal'),在右栏打开官方终端标签——与该面板自带 + 菜单里的「新建终端」是同一个 kind(kind 是 terminal;dsh-client-ui-sidebar-terminal 是包名,把它当 kind 传会抛错且什么都开不出来)。开的是官方终端本身,shell、工作目录、PTY 全归那个包,所以它只打开终端,不代跑任何命令:点哪一行都不影响终端的工作目录(终端从会话工作区启动),菜单标题因此写「工作区终端」而不是「在此处打开终端」。若当前环境没有注册终端面板,菜单会明确报「无法打开终端:宿主未提供终端面板」,不会假装开过。
Tab 补全(仅 FIM)
Tab 补全走 FIM(fill-in-the-middle):光标前的代码作 prompt、光标后的代码作 suffix 一起交给补全接口,服务端补出中间一段。没有提示词、没有对话框架,返回的就是代码本身,贴合度最高。
请求形态固定,一次请求一次响应,不流式:
POST {baseUrl}/completions # baseUrl 已以 /completions 结尾则原样使用
{ "model": …, "prompt": "<光标前>", "suffix": "<光标后>", "stream": false, "temperature": 0.2, "max_tokens": 128 }
⚠️ suffix 这个键必须始终存在,即使它是空串。DeepSeek 按请求体的形状分流:一旦这个键消失,请求就不再被当成 fill-in-the-middle,而是退化成普通续写——模型于是接着写自己的训练数据(CSDN 教程页的「时间: … 浏览: …」、markdown 围栏那一整套),而不是写代码。实测同一提示词的 A/B:省略该键 0/4 干净,显式传空串 8/8 干净。所以光标在文件末尾(后缀为空)时走的是 "suffix": "",不是不发这个字段。
为什么不用流式:ghost text 只会以「完整建议」的形式出现——Monaco 的 inline-completions provider 只答复一次,无法修订已经返回的结果——所以渐进渲染拿不到任何收益,反而要多养一个 SSE 解析器和一套帧协议。实测两者在噪声范围内没有差别。真正占时间的是首字节(TTFT),400–800ms 的网络 + 服务端排队,客户端改不动。
响应对 choices[0] 依次识别 text、message.content、delta.content 三种字段,因此 DeepSeek 之外的兼容服务也能直接用。请求带 10 秒硬超时;打字打断会立即中止在途请求。
触发的主体是 Monaco:它在每个输入字符(含空格)、退格/删除、Tab、粘贴上都会自己发请求。插件自己只补一种 Monaco 不问的情形:光标落点(鼠标点进某行、方向键挪过去)——停稳 250ms 后经 onDidChangeInlineCompletions 频道问一次,打字拖过去的光标移动、绘制选区中的落点、程序自己挪的光标都不算。此前插件在内容变更后 400ms 会自己发一次 editor.action.inlineSuggest.trigger,而那是 Explicit 触发,不满足已在途的 Automatic 请求——Monaco 会丢掉在途那次并重开一次,于是每次补全都多一个往返、固定多等 400ms;更糟的是每多敲一个字终点就再后退 400ms,表现就是「要打个空格或删一下才会出来」。
同一光标的并发请求现在共享一次往返:第二次询问 await 第一次的答案并优先读缓存,而不是回 {items: []}——空数组对 Monaco 不是「我不知道」而是「这一版没有建议」的判决,被记进 state 后后续询问会被短路,于是必须再动一次键盘才解锁。
空行上的补全:光标停在空行(或只含缩进的行)上时,模型经常回「先换行再缩进再写代码」——那是它在行尾的惯用续写,可空行本来就已经是新行,多出来的那一次换行会让 ghost 的第一行只剩下空格或缩进、按 Tab 也只插入一个换行加缩进。插件在空行上把这段冗余的前导空行折掉,让代码落回本行(落在行边界的建议本来就该先剥掉这个前导换行);折完仍然只剩空白的答案照旧当作「没有建议」。行尾光标上的「换行 + 缩进」是正确答案,原样保留。
设置项
在 DSH 设置的「代码工作台」里:
| 设置 | 默认值 | 说明 |
| 启用 Tab 补全 | 开 | 关掉后按 Tab 只说缩进,不发请求 |
| 补全接口地址 | https://api.deepseek.com/beta | 自动补 /completions;也接受完整端点(如 https://api.mistral.ai/v1/fim/completions) |
| 补全模型 | deepseek-flash | 该端点只接受 deepseek-flash 与 deepseek-v4-pro,两者都支持 FIM |
| 补全 API Key | 空 | 秘密字段,留空时不显示、不回显;填了就是最高优先级的凭据 |
| 编辑自动保存 | 关 | 打开后停顿 900ms 自动保存 |
设置页顶部有一块 「当前补全链路」,实际展示请求会落到哪个接口、哪个模型、用的是哪一路凭据——不用猜为什么没补全。
凭据优先级
按顺序尝试,第一个可用的胜出;任何一路都不可用时不报错,只是没有 ghost text:
1. 设置页填写的 API Key → 发送 Authorization: Bearer <key>
2. DSH 凭据库里名为 DEEPSEEK_API_KEY 的那条($DSH_HOME/.credentials.yaml 的 refs,同名的环境变量也算)→ 同样是 Bearer。分发出去时对方机器上只要他自己配过这个 key,就完全不用碰设置页。
3. DSH 登录账号的凭据授权(records.deepseek-account-platform/)→ 不发 Authorization,改发 DSH 私有头 x-dsh-auth-token。仅当目标地址的 origin 属于该账号服务信任的推理源(默认 https://api.deepseek.com)时才会签发。
API Key 通过 Host 设置服务保存,标记为秘密字段,设置页不会回显。密钥输入框留空保留原密钥。
失败时静默
接口地址不合法、找不到凭据、上游拒绝(401/403/5xx)、网络超时——这些都不会弹错:Host 半边记一条 logger.warn(含接口、模型、凭据来源),回给编辑器的是一个正常的 200 响应、只是 text 为空,编辑器不显示任何 ghost text,也不弹任何错误。只有请求体本身格式错误(即我们自己客户端半边坏了)才会返回 400。
编辑器状态栏会写一行结果:AI 补全生成中… / Tab 接受 AI 建议 / AI 未返回补全 / AI 补全已取消,便于现场判断到底走没走通。
所以排查补全完全不工作时,看 DSH 主机日志里的 code-workbench: completion skipped — …(那行带接口、模型和凭据来源),状态栏只告诉你「本地这一侧认为发生了什么」。
当前能力
| 能力 | 实现 | 机制 |
| 编辑器 | Monaco 内联(约 5.13 MiB bundle,懒加载) | monaco-editor 打进 bundle(浏览器模块表冻结,无 Monaco,必须内联) |
| Worker | Blob URL 内嵌 editor.worker | self.MonacoEnvironment.getWorker,无 CSP 拦截 |
| 文件树 | 递归、懒展开;行的类型字形取自 DSH 官方 UI 库(与官方工作区文件树同一个组件) | ctx.remote.workspaceFiles.list(官方只读 Remote);字形 = @deepseek-ai/dsh-client-ui-primitives 的 FileTypeIcon + classifyFileType——隐式 baseline 模块,只走 loader 的 require,不列依赖、也不进 dsh.client.external |
| 读文件 | 分页读全量、按扩展名选语言 | ctx.remote.workspaceFiles.read(按行分页,eof 收尾) |
| 非文本文件转交 | 点击 PDF / Excel / Word / PPT / 图片等非文本文件 → 不建模型、不开自家标签,改在右栏「文档预览」中打开 | 官方 fileAddressFor(@deepseek-ai/dsh-util-workspace-path)+ ctx.sidebarRight.openResource,落到官方 dsh-resource://file/ 预览档位;本插件 tab 类型不注册 pattern,因此不抢官方 fallback |
| 写文件 | POST /api/code-workbench/write,版本守卫(FS_STALE_VERSION 拒绝覆盖并发修改),沙箱策略 = 会话标准策略 | host 半体 ctx.connection.fetch.register(与官方 /api/file 同一通道)→ ctx.fs.writeText(target, text, intent, signal, sandboxPolicy) |
| 代码库检索 | 左栏「搜索」模式:正则/大小写搜索、结果列表、点击跳转到行并高亮 | POST /api/code-workbench/search:ctx.fs 有界遍历(忽略 node_modules/二进制扩展)+ 行匹配,文件数/匹配数/时间三重预算 |
| @codebase(Agent 工具) | 对话里的 Agent 自带 codebase_search 工具:自然语言多词排序检索,返回 top 片段(补官方 grep 的精确正则) | ctx.tools.register(官方 Tool Runtime)+ 同一检索核心(terms 模式 + 词覆盖率排序) |
| Tab 补全 | 打字停顿 180ms 后灰色 ghost 建议,Tab 接受、继续打字即取消;同一时刻最多一个请求 | Monaco registerInlineCompletionsProvider(ghost 渲染/接受全是内建)+ POST /api/code-workbench/complete(纯 FIM、非流式 prompt/suffix,128 token 预算,凭据三级兜底,10s 硬超时;失败静默)+ POST /api/code-workbench/completion-status(只读,回报实际链路) |
| 检查点 / 回滚 | 每次保存自动入账本;左栏「历史」列出检查点,一键回滚到任意保存之前;回滚本身也是检查点,可再回滚 | POST /api/code-workbench/history / rollback(host 内存账本,每文件 20 条、每侧 200KB 上限)+ ctx.fs.writeText 版本守卫 |
| 检查更新 / 一键升级 | 有新版发布时面板顶部出现提示条,点「升级」原地替换安装目录里的文件;只动了运行期不参与启动加载的文件(client.js,以及 src/ 构建输入、build.mjs、README 等)就免重启——client.js 由 HMR 自己重载面板;改了 Host 半边启动时加载过的文件(index.js 及其 import)才提示「重启 DSH 后生效」 | POST /api/code-workbench/update-check(api.github.com REST:/tags → 该 tag 的 update.json → 与本机逐文件摘要比对)与 POST /api/code-workbench/update-apply(每个文件先按清单摘要校验、再 rename 原子替换) |
| 外部改动自动重载 | Agent/外部改了盘上文件 → 干净缓冲区自动刷新;有未保存修改时警告不覆盖 | 官方 workspaceFiles.changes 流(ctx.remote.$stream),按打开文件订阅 |
| 加到对话(Add to Chat) | 右栏选中多行代码 → Ctrl+L、右栏按钮、或编辑器右键菜单「添加到对话」 → 主对话输入框里出现引用胶囊(只显示 文件:行号 · N 行),发送时才展开成完整代码块给智能体 | 官方 chip 机制:slash/input-insert-reference 事件插 ReferenceChipNode + 自注册 reference codec(inputTriggers.registerSource 的 codec.serialize 做提交展开)。三个入口进的是同一个 addToChat,所以产出同一枚胶囊;右键菜单项由 editor.addAction 注册在 cut/copy/paste 组之后,不带 keybinding(Ctrl+L 已由 addCommand 注册,重复注册同一个和弦是静默的)也不带 precondition(本 build 未定义的 context key 恒为 false,会把菜单项永久灰掉,看起来像坏了) |
| 选中悬浮胶囊 | 在编辑器里选中代码,选区附近浮出「加到对话 Ctrl+L」胶囊,点一下即加入对话;选区消失即收起,正在显示 ghost 建议时让位 | Monaco 内容部件(editor.addContentWidget)—— 胶囊只给出行列锚点,像素位置由 Monaco 自己算。落点搜索(在 ±2 行窗口里挑最长的一行作锚,代价 = 行长与 5 行之差 + 25 × 行偏移),但偏好恒为 EXACT、上下偏移由插件自算 transform:Monaco 的 ABOVE/BELOW 以整个窗口为钳制基准,而编辑器只占右栏一条 |
| 拖拽行到输入框 | 文件树行(文件或目录)拖进主对话输入框 → 落成引用胶囊;发送时展开为共享 @路径 mention(目录带尾斜杠、含空格走 @"…"),聊天记录里渲染回同一个胶囊 | 私有 MIME「三重收窄」在 document 捕获阶段接管拖放 → 同一个官方 slash/input-insert-reference 通道;提交展开走同一个 reference codec(clipboardText 与展开文案逐字一致) |
| 拖拽标签页 | 标签条上的标签同样可拖:拖进主对话输入框 → 与拖文件树行完全相同的引用胶囊;在标签条内左右拖 → 换位,落点那一侧的边缘出现插入标记 | 复用同一个私有 MIME 和同一个官方通道,因此胶囊只有一个出处;条内换位由各自的 drag ref 分流——两个手势互不认领(树行拖过标签条不换位,标签拖到文件夹行也不移动文件) |
| 点空白处取消选中 | 单击文件树空白处 → 清空选中;顶部新建按钮随之回到在工作区根目录创建 | 树的容器 onClick,用 closest('.code-workbench-tree-row, button, input, .code-workbench-viewbar') 排除落在行、行内控件和工具栏上的点击:行点击本来就会冒泡到容器,而工具栏是右对齐的,选中目录后的下一个动作就是往右上走点「新建文件」,擦边落在它的空白处——没有这道守卫时,两个手势会重合成一个,近失手又会把目标悄悄打回根目录。清空 selected 的同时一并清掉 Shift 多选集合与锚点 |
| 树只标记选中 | 文件树里只有选中(新建按钮的落点、右键菜单与拖动的作用目标)会画底色 + 细框;当前打开的文件在树里零标记 | 一次点击会同时打开文件并把它设为选中,两种状态叠在同一行:给它任何标记都会被读成「选中标记的一部分」——两个画得一模一样的框就是「取消不掉的框」,框上再叠一根竖条就是多了一个东西。哪个文件打开着由标签条回答(恰好一个活动标签),树不回答第二遍。:hover 保留——那是指针自己的瞬时反馈,不挂在任何状态上 |
| 可追溯 | 每次保存记 Session 备注 | sessionFeedback.record |
| 主题 | 跟随 DSH 明暗主题 | body[data-ds-dark-theme] MutationObserver → monaco.editor.setTheme;调色板取自 --dsw-alias-*,但先归一成 #RRGGBB[AA] 再交给 Monaco——它的主题解析只认 hex 拼写,认不出的值不会报错,而是静默涂成纯 #ff0000 |
| 快捷键 / 编辑器右键菜单 | Ctrl+S 保存、Ctrl+L 将选中代码加入对话、Tab 接受补全(编辑器内);编辑器右键菜单另有一项「添加到对话」 | editor.addCommand(快捷键)+ editor.addAction(菜单项,注册在 cut/copy/paste 组之后) |
组成与实现
插件由两个半边构成,DSH 分别加载:
| 半体 | 源码 → 产物 | 职责 |
| Host | index.ts → index.js | 版本守卫写盘、文件操作、检索、Tab 补全、检查点与回滚、检查更新与升级,以及 codebase_search 工具 |
| Client | src/client.ts → client.js | 右栏 tab code-workbench:文件树 + Monaco + Tab 补全 + 代码引用 |
为什么客户端半边必须内联 Monaco。 DSH 的客户端模块运行时把每个插件当一段自包含的脚本加载,工厂里只允许 require 基线模块表(react 等),Monaco 不在表里。所以 client.js 是把 Monaco、它的 worker 和全部 CSS 打包在一起的单文件(约 5.13 MiB,懒加载)——worker 转成 Blob URL 注入,CSS 转成 。这条契约是硬约束,不是取舍。
为什么 Host 半边不打包。 安装后的 index.js 是排障时唯一能读的东西,所以它由 tsc 逐文件转译、保留全部注释,而不是交给 esbuild(后者会剥掉所有注释)。类型错误会直接让构建失败,不会进入产物。
源码与产物同名同目录。 index.ts → index.js、src/client.ts → client.js,源码就放在它产出的东西旁边。少数源文件用 .mts 扩展名,因为 index.ts 里必须逐字写 ./completion-api.mjs——那是产物的真实名字,而 TypeScript 的扩展名替换规则里 .mjs 只映射 .mts。
SDK 的类型是手写的。 DSH 客户端 SDK 的每个包都在 package.json 里声明了 types 路径,但包里并没有那些文件,所以 types/ 下是这个插件真正调用到的那部分声明,范围刻意收窄——试图覆盖整个 SDK 只会得到它的一份无法校验的副本。
已知限制
- 文件同步使用官方 changes 流,并每 1.5 秒检查打开文件版本、窗口重新聚焦时检查;干净缓冲区自动重读,未保存修改保留。
- 新发送的代码引用在聊天记录中默认折叠为文件与行号,点击展开;模型仍收到完整选区。此前发送的普通代码块没有引用标记,保留原展示。
- 路径引用(右键「添加路径引用」或拖拽文件树行)在聊天记录里是与输入框同形的胶囊,点按在右栏预览——因为提交时展开的就是官方 @路径 mention,官方投影再把这段文字装饰回胶囊。目录胶囊只作展示:官方投影只给文件接跳转。
- 非文本文件(PDF、xlsx/xls/csv/tsv、doc/docx/ppt/pptx、图片等)不在本面板渲染:工作台只把文件地址转交给右栏的官方文档预览,因此它们的标签页不会出现在「代码工作台」里。转交依据是读取失败的错误码(workspace-file/not-text / too-large),不是后缀白名单——能渲染哪些后缀由官方预览自己的注册表拥有,这里不复制一份。若当前环境没有注册能认领该地址的预览实现(openResource 会抛错),工作台保留自己的错误提示并注明该去哪看,不会报告一个它没能打开的文件。
- 更新读的是 api.github.com REST 接口(/tags、/contents、/git/blobs),不走 raw 内容域——后者在部分网络下不可达。仓库没有 Release 对象,所以版本从 tag 列表里取:这里自己按 semver 排序(/tags 不承诺顺序),取最高的那个带 update.json 的 tag,因此本特性之前发布的 tag 会被逐个跳过而不是让整次检查失败。GitHub 不可达、仓库里没有清单、本地已是最新——三者对面板都只有一个答案:不显示任何东西。从 npm 安装的副本同样靠 GitHub 上的 tag 检查更新,因为清单是随仓库发布的。
- 升级只写本包自己的安装目录,不触碰工作区。清单里出现绝对路径、./.. 段、反斜杠或空段会被直接拒绝;文件数上限 64、单文件上限 32 MiB、清单上限 256 KiB。
- 替换是逐文件的 rename:一个文件失败不影响已经落地的那些,失败时留下的是「逐个都合法但版本混合」的树,而不是写坏的文件。校验用的是发布方在清单里给出的摘要(用 rename 而不是直接写,是因为安装树里每个文件都是 pnpm 硬链接到内容库的,写穿会改到其他 profile 共用的那份)。
- ⚠️ 若某次更新提示「重启 DSH 后生效」而用户没有重启就关掉了面板:不会再次提示——installedVersion() 读的是盘上的 package.json,而那时盘上已经是最新版了。这是刻意的(盘上版本就是已安装版本,不制造「永远提示有更新」的假信号),代价是这次提醒是一次性的;下次启动 DSH 会让新版本全面生效。
- 免重启的判定只看 client.js / package.json 是否在改动之列;只要发布里动了 Host 半边加载过的文件(index.js、completion-api.mjs、vendor/schemastery.mjs、src/completion-window.mjs、cordis.patch.yml),就提示重启——那些代码进程已经加载在内存里,替换文件本身不足以让它们生效。
- 拖放的两个手势共用同一个私有 MIME,靠各自的 drag ref 分流,所以跨手势不生效:把标签拖到文件树的文件夹行上不会移动文件,把文件树行拖过标签条也不会换位。要移动文件请用树内拖放或右键剪切/粘贴,要换位请在标签条内拖。
- 文件树的「选中」和编辑区的「当前文件」是两件事:清空选中不会关闭任何标签;从标签条打开一个文件也不改变树里的选中。但在树里点一行会同时把该行设为选中(新建按钮的落点始终跟着选中走),所以点开一个文件后那一行带的是选中标记,不是「当前文件」标记。树里不给当前打开的文件画任何东西。选中的目录不会被自动展开。
- Tab 补全采用 180ms 请求防抖、较小的前后文(前缀 3200 字符 / 后缀 900 字符)和 128 token 预算,并缓存最近 40 个光标上下文。⚠️ 本机网络抖动实测 ±300ms,比大部分参数效应还大——任何「提速了」的判断都要多轮取样才作数,单次测量没有意义。
- 凭据第三级(DSH 账号授权)已对真实 /beta/completions 端点验证通过(HTTP 200,用私有头 x-dsh-auth-token、不带 Authorization)。注意它只在 origin 属于该账号服务信任的推理源时才会签发。
- bundle 5.13 MiB(Monaco 内核 minify 后 + 常用 28 种语法 + JSON 语言服务);首次打开面板时解析(懒加载)。
- TS/JS 语义智能未启用(砍掉了 12MB 的 TS 语言服务):TS/JS 有语法高亮 + 编辑器全套功能,但没有语义诊断/跳转;补全为词级 + AI ghost text。
- 单文件读取受官方 workspaceFiles 上限约束(默认 2 MiB / 5000 行一页,最多 40 页)。
- 检查点账本在 host 内存(每文件 20 条):重启 DSH 后清零。
- 「语义检索」为词覆盖率排序的词汇检索(DeepSeek API 无 embeddings 端点,无法做向量语义)。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
FinalLawer/dsh-native-code-workbench
DeepSeek Harness 桌面端的原生 AI 代码工作台插件,提供文件树、Monaco 编辑器、Tab AI…
相关插件 · 工具 / 效率
破甲管控台kingcharleslzy-ai/dsh-purge
dsh-purge Community · DeepSeek Harness 社区维护版:宿主兼容、状态诊断、提示词管理、更新与本机备份回滚。基于小杨 / YuJunZhiXue 的 dsh-purge。
★ 4,330已汉化DeepSeek 客户端
74.5
网文长卷lingfengQAQ/webnovel-writer
基于 Claude Code 的长篇网文辅助创作系统,解决 AI 写作中的「遗忘」和「幻觉」问题,支持 200…
★ 7,636已汉化DSH
72.2
多智能体协作HKUDS/AutoSwarm
AutoSwarm: Unify Your Agents. Unify Your Context (Codex · Claude Code · DeepSeek Harness · Muse · Grok · WorkBuddy· nanobot · and more)
★ 2,349已汉化DSH
71.6
Vox 拼贴视频生成Alisa0808/vox-director
🎬 Vox Director拼贴动效导演
★ 2,129已汉化DSH
71.4
Agent 演化引擎Q00/ouroboros
智能体 OS:智能体自己变聪明,我们只守住底线:面试准入、分阶段评估、带预算的进化循环。MCP 服务器,14 种运行时:Claude Code、Codex CLI、Gemini CLI、OpenCode、Copilot、Kiro 等。
★ 6,311已汉化DSH
71.4
智能体运行框架路由HarnessRouter/harnessrouter
智能体 harness 的统一接口。
★ 3,984已汉化DSH
71.3
这是你的仓库?认领条目