Bundle
dsh-workspace-view
DeepSeek Harness workspace file-tree drawer plugin
- Source
- N5Tar
- License
- MIT
- Updated
- Updated 3 days ago
Readme
<p align="center">
<img src="./assets/dsh-workspace-view-logo.png" width="180" alt="dsh-workspace-view logo">
</p>
<h1 align="center">dsh-workspace-view</h1>
<p align="center"><strong>简体中文</strong> · <a href="./README_en.md">English</a></p>
`dsh-workspace-view` 是一个面向 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Web GUI 的工作区文件浏览插件。它在页面右侧提供一个默认展开的文件树抽屉,用于查看当前会话所属工作区的目录与文件。
## 功能特性
- 在页面右侧显示全高文件树抽屉
- 根据当前 Session 自动切换工作区
- 按需加载目录内容,避免启动时递归扫描整个仓库
- 目录优先、文件随后,并按名称排序
- 支持展开和折叠文件夹
- 支持刷新当前文件树
- 支持显示或隐藏点文件
- 关闭后收缩为页面右侧的窄把手
- 提供中文和英文界面文案
- 使用 DSH Theme Token,自动适配宿主主题
- 包含加载、空目录、无会话、截断和错误状态
## 运行要求
- Node.js 版本应符合 DeepSeek Harness 的运行要求
- pnpm
- 用于安装和运行插件的 DeepSeek Harness 环境
插件自身的安装、测试和构建使用已发布的依赖版本,不要求源码仓库与 `deepseek-harness` checkout 并列放置。只有将本地包安装到 DSH 或使用临时 Patch 启动时才需要提供 Harness 路径。
## 设计与架构
本插件遵循 DSH 的 Cordis 插件模型,由 Host 和 Client 两部分组成。
### Host
Host 插件注入以下服务:
- `webServer`:注册只读目录查询接口
- `sessions`:根据 Session id 获取由 Host 管理的权威工作区路径
Host 不接受客户端指定工作区根目录。每次目录查询都会:
1. 根据 Session id 从 Session Store 获取 `cwd`。
2. 对工作区根目录和目标目录执行 `realpath`。
3. 使用 `relative` 验证目标目录没有越出工作区。
4. 打开目录后再次校验真实路径,拒绝验证与打开阶段之间发生的路径替换。
5. 按配置限制扫描量和返回条目数。
6. 只向客户端返回稳定的公共错误信息。
该流程可阻止 `..` 路径穿越和符号链接逃逸。
### Client
Client 插件注册到 additive `shell.overlay` Slot,在页面右侧渲染文件抽屉。它不会注册或替换以下 Slot:
- `sidebar`
- `sidebar.workspaces`
- `sidebar.settings`
因此不会移除或覆盖 DSH 原有的工作区、会话和设置界面。
当前 Session 和工作区路径来自 Slot 提供的 `useSessions` 标准属性。目录在用户展开时按层请求,并会取消已经失效或被刷新取代的请求。
## 安装
### 安装到 Web Profile
先完成依赖安装和构建:
```sh
cd /path/to/dsh-workspace-view
pnpm install
pnpm run build
```
使用 DSH 插件管理命令将本地包安装到 Web Profile:
```sh
cd /path/to/deepseek-harness
pnpm dsh plugin --profile web add /path/to/dsh-workspace-view
```
检查最终配置:
```sh
pnpm dsh --profile web --dump-config
```
启动 Web GUI:
```sh
pnpm dsh web
```
默认访问地址:
```text
http://127.0.0.1:3080
```
如果 Web GUI 已经运行,需要重启原进程才能加载新的 Host 插件和 Client manifest。
### 移除插件
```sh
cd /path/to/deepseek-harness
pnpm dsh plugin --profile web remove dsh-workspace-view
```
## 本地开发
### 临时 Patch 方式
如不希望写入 Web Profile,可以将本包链接到 Harness 的依赖解析目录,并通过临时 Patch 启动:
```sh
cd /path/to/deepseek-harness
mkdir -p node_modules
ln -sfn /path/to/dsh-workspace-view node_modules/dsh-workspace-view
pnpm dsh web --patch /path/to/dsh-workspace-view/cordis.patch.yml
```
### 常用命令
```sh
# 安装依赖
pnpm install
# TypeScript 类型检查
pnpm run typecheck
# 运行测试
pnpm test
# 构建 Host 和 Client 产物
pnpm run build
# 验证已提交的 lib 产物与源码一致
pnpm run check:dist
# 检查发布内容;prepack 会先重新构建
pnpm pack --dry-run
```
### 重新加载说明
本项目是 Harness checkout 之外的外部插件,不属于 Harness 自带 `pnpm run dev:web` 的 workspace 扫描范围。
- 修改 Client 代码后:先运行 `pnpm run build`。对于外部插件,可靠的生效方式仍是重启当前 DSH Web 进程,再刷新页面。
- 只有在已确认 `pnpm run dev:web` watcher 正在重建本插件,并且其 HMR receiver 连接到当前 DSH 进程时,才能依赖自动更新。
- 修改 Host 代码、插件配置或 `package.json` 中的 `dsh.client` 元数据后:必须重启 DSH Web。
- 单独启动另一个 Vite server 不会重算当前进程的 Client manifest,也不会更新已经运行在 `127.0.0.1:3080` 的 GUI。
## 配置
插件配置位于 `cordis.patch.yml`:
```yaml
- insert:
- id: workspace-view
name: dsh-workspace-view
config:
maxEntries: 500
maxScannedEntries: 10000
maxConcurrentScans: 8
ignoredNames:
- .git
- node_modules
```
| 字段 | 类型 | 默认值 | 约束 | 说明 |
| --- | --- | --- | --- | --- |
| `maxEntries` | `number` | `500` | `1`–`5000` | 每层最多返回给 Client 的条目数 |
| `maxScannedEntries` | `number` | `10000` | `1`–`50000` | Host 每层最多扫描的目录项数量,用于限制超大目录的 CPU 与内存消耗 |
| `maxConcurrentScans` | `number` | `8` | `1`–`64` | Host 同时执行的目录扫描上限;超出时返回 HTTP 429 |
| `ignoredNames` | `string[]` | `['.git', 'node_modules']` | — | Host 完全忽略的文件或目录名称 |
`ignoredNames` 与隐藏文件开关的行为不同:
- 被 `ignoredNames` 匹配的条目不会由 Host 返回。
- 其他点文件仍会返回,但默认在界面中隐藏,可通过抽屉工具栏切换显示。
当扫描或返回结果超过上限时,Client 会在对应目录层级显示截断提示。
## 安全模型
插件只提供工作区范围内的只读目录列表能力,不提供文件写入、删除、重命名或执行功能。
主要安全约束:
- 工作区根目录由 Host Session Store 决定
- 客户端不能自行指定根目录
- 常规路径和静态符号链接解析必须位于工作区的真实路径之下
- 打开目录后再次验证真实路径,降低验证与枚举之间的并发路径替换风险
- 受 Node.js 跨平台目录 API 限制,该检查不是 `openat` 式原子能力边界;不应把可由不受信任本地进程并发改写的工作区作为强隔离沙箱
- Host 对单次扫描、返回数量和并发扫描数均设有明确上限
- HTTP 客户端中断请求后,Host 会停止对应扫描
- 原始文件系统错误不会直接暴露给浏览器
## 项目结构
```text
.
├── assets/
│ └── dsh-workspace-view-logo.png # 仓库 Logo
├── cordis.patch.yml
├── package.json
├── README_en.md
├── src/
│ ├── index.ts # Host 插件与目录查询接口
│ └── client/
│ ├── index.ts # Client Slot 注册
│ ├── controller.ts # 抽屉状态控制器
│ ├── locales.ts # 中英文文案
│ ├── WorkspaceFiles.tsx # 文件树与抽屉界面
│ └── WorkspaceFiles.module.css # 组件样式
├── tests/
│ ├── client.spec.tsx # 请求取消、状态保留和焦点测试
│ └── host.spec.ts # 路由、排序、截断和路径边界测试
├── .github/workflows/ci.yml # 类型、测试、构建和发布检查
├── LICENSE
├── tsconfig.json
└── tsdown.config.ts
```
构建产物输出到 `lib/`。`package.json` 通过 `dsh.bundle.patch` 声明随包安装的 Cordis Patch,并通过 `dsh.client` 声明 Web Client bundle 及其依赖图。
## 已知限制
- 当前版本只浏览文件树,不读取或预览文件内容。
- 文件系统发生变化后需要手动刷新。
- 大型目录达到配置上限时只显示部分条目。
- 抽屉通过 `shell.overlay` 实现,会覆盖页面最右侧区域,而不会参与 DSH 核心列宽求解。
- 对可由恶意本地进程并发替换的工作区路径,仍需未来的原子 Host 文件系统能力才能提供强隔离。
- 外部插件开发模式不具备 Harness workspace package 的完整自动重建体验。
## License
[MIT](./LICENSE)
Install
dsh plugin --profile web add github:N5Tar/dsh-workspace-view
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-workspace-view from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.