🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

工作台停靠面板 1Lry1/dsh-workbay

DeepSeek Harness
1Lry13 天前有提交已提供中文文档扫描:含敏感能力

在侧栏停靠面板中并排使用文件、终端、Git 与烧录页面

29.8/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/11/2 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add 1Lry1/dsh-workbay
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:仅索引
是什么
dsh 原生插件 · browser
装得上吗
本站尚未做安装检查
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 3 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件读取剪贴板或密钥环境变量写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 77 处,展示前 52 条)
  • 发起网络请求
    packages/downloader/http-test.mjs:43
    const r = await fetch(BASE + p)
  • 发起网络请求
    packages/downloader/http-test.mjs:47
    const r = await fetch(BASE + p, {
  • 发起网络请求
    packages/downloader/http-test.mjs:159
    const r = await fetch(BASE + '/events', { signal: ctrl.signal })
  • 发起网络请求
    packages/downloader/lib/client.js:113
    const r = await fetch(API + p, { headers: { accept: 'application/json' } })
  • 发起网络请求
    packages/downloader/lib/client.js:119
    const r = await fetch(API + p, {
  • 执行外部命令来自配置/变量
    packages/downloader/lib/index.js:336
    const child = spawn(
  • 执行外部命令来自配置/变量
    packages/downloader/lib/index.js:397
    this.proc = spawn(this.exe, this.args || [], {
  • 执行外部命令来自配置/变量
    packages/downloader/lib/index.js:2057
    try { return execFileSync(cmd, args, { encoding: 'utf8', timeout: 2000 }) } catch { return '' }
  • 执行外部命令来自配置/变量
    packages/downloader/smoke-test.mjs:86
    const child = spawn(process.env.GIT_EXE || 'git',
  • 执行外部命令来自配置/变量
    packages/downloader/smoke-test.mjs:151
    const p = spawn(SERIAL_HELPER, [], { stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true })
  • 执行外部命令来自配置/变量
    packages/downloader/smoke-test.mjs:188
    const p = spawn(IPC_HELPER, ['--exe', DL_EXE, '--args', 'BackMode {hwnd}'],
  • 读取本地文件
    packages/files/src/index.js:223
    return entryFromStats(rootReal, absPath, lstatSync(absPath))
  • 读取本地文件
    packages/files/src/index.js:248
    if (statSync(letter + ':\\').isDirectory()) {
  • 读取本地文件
    packages/files/src/index.js:710
    dirents = readdirSync(dir, { withFileTypes: true })
  • 读取本地文件
    packages/files/src/index.js:720
    try { isDirectory = statSync(path.join(dir, dirent.name)).isDirectory() } catch { isDirectory = false }
  • 读取本地文件
    packages/files/src/index.js:743
    dirents = readdirSync(absDir, { withFileTypes: true })
  • 读取本地文件
    packages/files/src/index.js:757
    stats = lstatSync(abs)
  • 写入本地文件
    packages/files/src/index.js:1022
    writeFileSync(abs, payload.content, { encoding: 'utf8' })
  • 写入本地文件
    packages/files/src/index.js:1055
    mkdirSync(abs)
  • 写入本地文件
    packages/files/src/index.js:1093
    renameSync(fromAbs, toAbs)
  • 删除本地文件
    packages/files/src/index.js:1128
    unlinkSync(abs)
  • 删除本地文件
    packages/files/src/index.js:1131
    if (code === 'EPERM' || code === 'EISDIR') rmdirSync(abs)
  • 删除本地文件递归
    packages/files/src/index.js:1136
    rmSync(abs, { recursive: true, force: false })
  • 删除本地文件
    packages/files/src/index.js:1139
    rmdirSync(abs)
  • 删除本地文件
    packages/files/src/index.js:1149
    unlinkSync(abs)
  • 发起网络请求
    packages/gitcraft/src/client.js:485
    response = await fetch(apiBase() + method, {
  • 执行外部命令来自配置/变量
    packages/gitcraft/src/index.js:154
    child = spawn(gitBinary(), [...GIT_GLOBAL_ARGS, ...args], {
  • 执行外部命令来自配置/变量
    packages/gitcraft/src/index.js:218
    child = spawn(gitBinary(), [...GIT_GLOBAL_ARGS, ...args], {
  • 读取本地文件
    packages/gitcraft/src/index.js:492
    const stat = lstatSync(path.join(dir, '.git'))
  • 读取本地文件
    packages/gitcraft/src/index.js:511
    entries = readdirSync(dir, { withFileTypes: true })
  • 读取本地文件
    packages/gitcraft/src/index.js:548
    entries = readdirSync(dir, { withFileTypes: true })
  • 读取本地文件
    packages/gitcraft/src/index.js:610
    if (statSync(root).isDirectory() === true) roots.push(root)
  • 读取本地文件
    packages/gitcraft/src/index.js:825
    stats = lstatSync(dotGit)
  • 读取本地文件
    packages/gitcraft/src/index.js:832
    text = readFileSync(dotGit, 'utf8')
  • 删除本地文件
    packages/gitcraft/src/index.js:1618
    unlinkSync(absolute)
  • 发起网络请求
    packages/gitcraft/src/index.js:1914
    async fetch(payload) {
  • 读取本地文件
    packages/kit/src/host.js:318
    rootStats = statSync(rootReal)
  • 读取本地文件
    packages/kit/src/host.js:334
    stats = lstatSync(next)
  • 发起网络请求
    packages/terminal/src/client.js:275
    response = await fetch(apiBase() + method, {
  • 读取剪贴板
    packages/terminal/src/client.js:1718
    function readClipboard() {
  • 读取剪贴板
    packages/terminal/src/client.js:1722
    navigator.clipboard.readText().then((text) => {
  • 写入剪贴板
    packages/terminal/src/client.js:1745
    navigator.clipboard.writeText(selection).catch(() => {})
  • 读取剪贴板
    packages/terminal/src/client.js:1757
    readClipboard()
  • 读取本地文件
    packages/terminal/src/index.js:287
    if (statSync(candidate).isFile()) return canonicalPath(candidate)
  • 读取本地文件
    packages/terminal/src/index.js:338
    stats = statSync(configured)
  • 读取本地文件
    packages/terminal/src/index.js:387
    stats = statSync(requested)
  • 读取本地文件
    packages/terminal/src/index.js:418
    exists = statSync(target).isFile()
  • 执行外部命令固定命令
    packages/terminal/src/index.js:576
    const killer = spawn('taskkill', ['/T', '/F', '/PID', String(pid)], {
  • 执行外部命令来自配置/变量
    packages/terminal/src/index.js:819
    child = spawn(binary, argv, {
  • 读取本地文件
    packages/terminal/src/index.js:995
    stats = statSync(resolved)
  • 执行外部命令来自配置/变量
    packages/terminal/src/index.js:1029
    child = spawn(session.shellProfile.binary, argv, {
  • 读取本地文件
    packages/terminal/src/index.js:1352
    if (!existsSync(chunksDir)) throw new ApiError('not-found', 'no chunks have been built', 404)
扫描于 2026/11/4(0 天前) · 范围 全包扫描 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

DSH workbench:一个 dock 宿主,外加 GitCraft 和 Downloader 两个页面、一套共享宿主工具包,以及一个一次性打包的套件包。

README

由 DeepSeek 最新模型翻译生成
dsh-workbay DeepSeek Harness(DSH)的工作台:侧栏一个入口,右侧一块固定面板,文件、Git、终端、硬件烧录四个页面并排使用,与具体会话无关。 这个仓库把能力收敛成一个 pnpm workspace: packages/ ├─ dock/ dsh-workbay-dock 壳:左侧栏入口 + 右侧停靠浮层 + 页面注册表 ├─ files/ dsh-workbay-files 页面:文件树 + VSCode 级内容搜索 + 查看器 + CodeMirror 编辑器(chunk 懒加载) ├─ terminal/ dsh-workbay-terminal 页面:真终端(ConPTY)+ 多 tab(xterm.js,切页保持会话) ├─ gitcraft/ dsh-gitcraft 页面:SourceTree 级 Git 面板(58 个 RPC) ├─ downloader/ dsh-downloader 页面:蓝牙芯片烧录 + 1.5 Mbps 串口日志 ├─ kit/ @dsh-workbay/kit 共享 host 工具:同源围栏 / JSON 体 / RPC 信封 └─ suite/ dsh-workbay-suite 单插件形态:vendor 五个 bundle + 合并 client,插件列表只显示一个
展开完整文档(README 剩余 1 节)
dsh-workbay

DeepSeek Harness(DSH)的工作台:侧栏一个入口,右侧一块固定面板,文件、Git、终端、硬件烧录四个页面并排使用,与具体会话无关。

这个仓库把能力收敛成一个 pnpm workspace:

packages/
├─ dock/ dsh-workbay-dock 壳:左侧栏入口 + 右侧停靠浮层 + 页面注册表 ├─ files/ dsh-workbay-files 页面:文件树 + VSCode 级内容搜索 + 查看器 + CodeMirror 编辑器(chunk 懒加载) ├─ terminal/ dsh-workbay-terminal 页面:真终端(ConPTY)+ 多 tab(xterm.js,切页保持会话) ├─ gitcraft/ dsh-gitcraft 页面:SourceTree 级 Git 面板(58 个 RPC) ├─ downloader/ dsh-downloader 页面:蓝牙芯片烧录 + 1.5 Mbps 串口日志 ├─ kit/ @dsh-workbay/kit 共享 host 工具:同源围栏 / JSON 体 / RPC 信封 └─ suite/ dsh-workbay-suite 单插件形态:vendor 五个 bundle + 合并 client,插件列表只显示一个

左侧栏 [工作台] ──点击──▶ 右侧停靠面板
┌────────────────────────────────────────┐
│ 工作台 已停靠右侧 ⤢ ⇥ │
│ [文件] [终端] [GitCraft] [Downloader] │
│ │
│ 当前页面内容(与会话并排) │ └────────────────────────────────────────┘

为什么不是一个大插件

三个 bundle 的故障域、发布节奏和分发对象都不一样,合并成一个包会把这些边界焊死:

| 维度 | dock(壳) | gitcraft(页面) | downloader(页面) |
| host 侧 | 2 个只读方法 | 58 个 RPC,git 子进程 | 串口 + 烧录 IPC + SSE,拉起两个 exe(含 32 位桥) |
| 依赖 | 无 | 本机 git | 官方 Downloader、CP210x 驱动、真实板卡 |
| 更新节奏 | 稳定(0.2.x) | 快(0.1.x 持续迭代,13 个 e2e) | 随硬件/固件变化 |
| 面向 | 想挂页面的第三方 | 社区(可独立发 npm) | 内部工具 |
| 引擎范围 | >=0.2.1-rc.1 <0.3.0-0 | >=0.1.6-alpha.2 | >=0.2.1-rc.1 <0.3.0-0 |

一个 bundle 起不来可能让整棵插件树启动失败(duplicate prefix route 就会),所以烧录侧的异常不该带崩 Git 面板——反过来也一样。

页面协议

页面是浏览器侧注册的 React 组件,注册表放在一个浏览器全局上,两个 bundle 的加载顺序无所谓:

window.__DSH_WORKBAY__.registerPage({
id: 'myplugin', // 必填,非空 title: '我的页面', order: 40, // 升序,GitCraft=20,Downloader=30
icon: (size) => <Icon size={size} />,
component: ({ t, dock }) => <MyPanel />,
})

若 dock 还没加载,短轮询等它出现(现有两个页面插件都这么做)。完整约定与三个已被踩过的坑见 packages/dock/README.md。

给别人装(从 GitHub 克隆)

本项目刻意不发布 npm 包:GitHub 源码就是唯一分发方式,别人克隆后按下面四步装。目标机器要求:

| 前置 | 要求 |
| 系统 | Windows 10/11(downloader 依赖两个 Windows helper exe 和 COM 串口) |
| DSH 桌面版 | >=0.2.1-rc.1 <0.3.0-0 |
| Git | 任意较新版本(gitcraft 会调用本机 git) |
| Node.js / pnpm | Node >=18;pnpm 11(corepack enable 会按 package.json 的 packageManager 取到 11.28.5) |

1) 克隆并装好 workspace 依赖
git clone https://github.com/1Lry1/dsh-workbay.git D:\tools\dsh-workbay
cd D:\tools\dsh-workbay
pnpm install

2) 生成要粘贴的 profile 片段(路径已按克隆位置展开)

pnpm run profile:snippet
powershell

3) 找到自己的 profile(默认叫 desktop;不确定就列一下)

Get-ChildItem $env:USERPROFILE\.dsh\profiles

编辑 <profile>\package.json:把脚本打印的 bundles 三行和 dependencies 四项粘进去

powershell

4) 装进 profile,然后“完全退出” DSH 再打开

cd $env:USERPROFILE\.dsh\profiles\desktop
pnpm install

左侧栏出现「工作台」即成功。

downloader 页额外需要(不设也能装,只是烧录不可用): powershell
[Environment]::SetEnvironmentVariable('DSH_DOWNLOADER_DIR', '<厂商 Downloader 安装目录>', 'User')
[Environment]::SetEnvironmentVariable('DSH_DOWNLOADER_WORKSPACE', '<SDK 工程根目录>', 'User')

之后更新用 pnpm run update(见下)。如果哪天想要 dsh plugin add 一行安装,再考虑发 npm 或做 Release tarball 即可。

安装到 DSH

profile 目录:C:\Users\<user>\.dsh\profiles\<profile>(本机是 desktop)。

方式 A —— 单插件(推荐:插件列表只显示一个):装 dsh-workbay-suite,它是把 5 个 bundle 物理收进来的单包(vendor/ + 合并的 host/client)。
jsonc
{
"dsh": { "profile": { "bundles": [
"@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app",
"dsh-workbay-suite"
] } },
"dependencies": {
"dsh-workbay-suite": "file:<repo>/packages/suite"
}
}
powershell
pnpm run build # 生成 chunk + suite 的 vendor/client/patch(gitignored)
pnpm install # 或 dsh plugin --profile desktop add file:<repo>/packages/suite
pnpm run verify:profile # 校验安装副本完整、且没有多余的 workbay 包

方式 B —— 五个独立 bundle(开发用,插件列表显示 5 个):package.json 里 jsonc {
"dsh": { "profile": { "bundles": [
"@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app",
"dsh-workbay-dock", "dsh-workbay-files", "dsh-workbay-terminal",
"dsh-gitcraft", "dsh-downloader"
] } },
"dependencies": {
"dsh-workbay-dock": "file:<repo>/packages/dock",
"dsh-workbay-files": "file:<repo>/packages/files",
"dsh-workbay-terminal": "file:<repo>/packages/terminal",
"dsh-gitcraft": "file:<repo>/packages/gitcraft",
"dsh-downloader": "file:<repo>/packages/downloader",
"@dsh-workbay/kit": "file:<repo>/packages/kit"
}
}

两种方式不要混用:同一条 loader 行插两次会让前缀路由重复注册,整棵插件树起不来。
pnpm run verify:profile 会拦住这种配置。

装完重启 DSH,左侧栏出现「工作台」。

@dsh-workbay/kit 必须能被 DSH 的运行时解析到:dock 和 gitcraft 的 host 半侧会 import 它,所以 profile 的 node_modules 里要有它。用 pnpm install 时 pnpm 会按 file: 依赖自动装好;手工拷贝插件目录时容易漏,表现为 host 半侧加载失败。

在 DSH 关闭后自动安装(推荐)

refresh-profile.ps1 不能在 DSH 运行时执行:应用锁着插件目录,而且工作台里的终端本身就是 DSH 的子进程。所以在工作台终端里直接跑 pnpm run install:profile 必然会看到 close DSH first, then run this script again——这是保护,不是故障。

用这个入口可以省掉"先关再手动跑"的来回: powershell
pnpm run install:profile:on-exit

它启动一个 WMI 分离进程等待 DSH 退出,然后自动构建 + 安装。

现在完全退出 DSH(含托盘),装完再打开 DSH 即可;日志在 %TEMP%\dsh-workbay-install.log

更新到最新版(从 GitHub)

插件以本机克隆为安装源(file: 是拷贝,不是软链),所以「用 GitHub 上的版本」= 拉取克隆 + 重装 profile: powershell

1) 完全退出 DSH(不是最小化)

2) 在克隆目录里:

cd <repo>
pnpm run update # git fetch + ff-only + 跑测试 + 重装 profile
pnpm run update:fast # 同上,跳过测试

pnpm run update 只做 fast-forward:工作区脏或有本地提交时会拒绝执行,不会偷偷合并。
它和 install:profile 一样,会在 DSH 还开着、或 AbSerialHost.exe / DownloaderIpcBridge.exe 还占着文件时中止(pnpm 会报 ERR_PNPM_EPERM)。

开发 powershell
pnpm install # workspace 依赖
pnpm test # 所有包的单测/集成测
pnpm --filter @dsh-workbay/kit run test
pnpm --filter dsh-gitcraft run test

浏览器 e2e(需要运行中的 DSH + 一个 Chromium 内核浏览器): powershell
node packages/gitcraft/tests/ui.e2e.mjs <browser-exe> <repo>
node packages/dock/tests/split.e2e.mjs <browser-exe> <repo> [shot.png]
node packages/downloader/smoke-test.mjs # 25 项自检;先设 DSH_DOWNLOADER_* 环境变量,未设置的用例自动 SKIP

设计约束(别踩)

1. 客户端半侧不能 import 本仓库的包。DSH 用 window.__ModuleLoader__.load({ id, factory }) 加载客户端插件,一个插件就是一个自包含文件 + 宿主 require 种子表。所以页面注册那段小代码在两个页面插件里各留一份,这是刻意的。
2. @dsh-workbay/kit 只服务 host 半侧。它不导出任何浏览器可用的东西。
3. downloaders 不用 kit 的 RPC 信封:它的 host 还要输出 SSE 与二进制帧,硬套会降低可读性。
4. 不要同时挂 suite 和单包,会重复 insert 同一行导致整棵树启动失败。
5. 每个包的 cordis.patch.yml 里都只有一条 insert;id 是全局唯一的 loader id。

常见问题

改了 packages/* 但界面上没变化 — file: 依赖是拷贝进 profile 的 node_modules,不是软链。重启 DSH 前必须先重装 profile: powershell

先退出 DSH(不是最小化),再在克隆目录执行

pnpm run install:profile

或:cd $env:USERPROFILE\.dsh\profiles\desktop; pnpm install

pnpm install 报 ERR_PNPM_EPERM ... rename ... dsh-downloader — downloader 的串口 helper AbSerialHost.exe(烧录时会再起 DownloaderIpcBridge.exe)还活着,文件被占用。退出 DSH 后它们会跟着退出;顽固残留就手动结束:
powershell
Get-Process AbSerialHost, DownloaderIpcBridge -ErrorAction SilentlyContinue | Stop-Process

界面里「工作台」有了但没有页面 — 页面注册表是浏览器全局 window.__DSH_WORKBAY__,两个页面插件都在等它。若 dock 没被加载(bundles 里漏了 dsh-workbay-dock),页面就无处注册;反之若页面包漏了,Tab 条只有工作台本身。检查 profile 的 dsh.profile.bundles 三行齐全。

host 半侧加载失败、日志报 Cannot find package '@dsh-workbay/kit' — profile 的 node_modules 里缺 kit。pnpm install 会按 file:../kit 自动带上;手工拷贝插件目录时最容易漏。

参与与安全

- 开发流程、目录约定、页面注册契约和提交规范:CONTRIBUTING.md
- 漏洞报告渠道与威胁模型(路由只对 loopback 开放、插件按设计在本机执行 git/串口操作、无遥测):SECURITY.md
- CI(.github/workflows/ci.yml):Windows 上跑 pnpm test,Linux 上做一遍 node --check 语法门禁

License

MIT
预览分享卡片长什么样 ↓1Lry1/dsh-workbay
分享 · DeepSeek Harness Hub
1Lry1/dsh-workbay

在侧栏停靠面板中并排使用文件、终端、Git 与烧录页面

这是你的仓库?认领条目

回到顶部