Bundle
dsh-workspace-native-open
DeepSeek Harness Web 工作区菜单增强:loopback 访问时提供「在文件资源管理器打开」「在终端打开」「在 Code 打开」三个动作。
- Source
- JoeyLearnsToCode
- License
- MIT
- Updated
- Updated 3 days ago
Readme
# dsh-workspace-native-open
DeepSeek Harness (DSH) 的**工作区菜单增强**插件:通过 loopback 地址访问 Web GUI 时,工作区行的 ⋯ 菜单会增加三个本机动作——**在文件资源管理器打开**、**在终端打开**、**在 Code 打开**(在宿主机器上执行)。
[English](README.en.md)
## ✨ 功能
| 功能 | 说明 |
| --- | --- |
| 在文件资源管理器打开 | 用系统文件管理器打开工作区目录(`explorer.exe` / `open` / `xdg-open`,含 WSL 路径翻译) |
| 在终端打开 | 在工作区目录打开新终端窗口。Windows:`pwsh` > `powershell`(经 `where.exe` 探测;Windows 10+ 必带 PowerShell);WSL:路径经 `wslpath` 翻译后交 Windows 控制台打开;其他平台:`$TERMINAL` > `bash` |
| 在 Code 打开 | 始终展示——插件**从不检测** VS Code 是否安装,点击后直接尝试打开目录,失败静默忽略(按需求)。WSL 下优先用 Linux 侧 `code` 脚本,不存在才走翻译路径上的 `code.cmd` |
| 仅 loopback 生效 | 页面地址为 `localhost` / `127.0.0.1` / `::1` / `[::1]`(以及内建放行的 `dsh.localhost`)时才出现菜单项;宿主端点同时拒绝 `Host` 头不在白名单内的请求——远程或反代访问绝不暴露本机动作 |
| 静默运行、按需留痕 | 失败不打扰你,但每次请求、探测结果、spawn 失败原因、launcher 退出码都会以 `workspace-native-open` 前缀写入 harness 日志便于排查 |
| 路径分隔符安全 | 路径全程以 `argv` 数组传递(从不拼接 shell 字符串);Windows 下 `/` 与 `\` 均可;PowerShell 单引号字面量保护空格与特殊字符 |
## 📦 安装
### 方式 A:直接从 GitHub 安装
```sh
dsh plugin --profile web add github:JoeyLearnsToCode/dsh-workspace-native-open
```
### 方式 B:本地链接(开发)
```sh
dsh plugin --profile web add link:/path/to/dsh-workspace-native-open
```
### 方式 C:手动部署
1. 让包可被 harness 解析(例如放进 `node_modules`),并在 profile 的 `cordis.patch.yml` 中注册:
```yaml
- insert:
- id: workspace-native-open
name: 'dsh-workspace-native-open'
```
2. 重启 `dsh web`
> 本插件面向 Web profile(`dsh --profile web`),依赖 `dsh-web-app` bundle 提供的 `webServer` 服务。
## 🔧 工作原理
单一宿主插件,两部分:
- **宿主侧** —— 在 `webServer` 服务上注册 `POST /api/plugin/workspace-native-open`。处理器拒绝 `Host` 头不在 loopback 白名单内的请求,校验 action 白名单、强制 JSON content-type(与 `/api/*` 相同的 CSRF 防线),并确认路径是存在的绝对目录后,fire-and-forget 执行本机打开动作。请求体有限长 + 限时(413/408)。
- **客户端侧** —— 订阅 `webserver/index-inject` 事件,向每个 `index.html` 注入一段经典 script 与样式。script 只在白名单内的 loopback 域名下激活(与宿主侧同一份名单,构建时内联):捕获工作区 ⋯ 按钮(以 `aria-label` 识别——dsh 未给该按钮/菜单任何稳定 id/class/data 属性,CSS Modules 类名带内容哈希)的点击,与紧随其后出现的 portal 菜单做点击因果配对,不做菜单文案扫描。菜单项语言取自 `<html lang>`(dsh 的「语言」设置实时同步),文案构建时内联自 `package.nls.zh.json` / `package.nls.en.json`,样式复用菜单卡片自身的 CSS 变量。
工作区行 → 路径的映射从行的 `aria-label` 提取工作区名,与 `/api/workspace.list` 返回的 `title` 精确匹配——搜索过滤、增删、重排都不会错位;仅同名工作区或 label 格式变化时才退回 DOM 顺序对齐(`ui-workspace` 按注册顺序渲染分组,未分组行没有菜单按钮)。
### 平台要点
- **Windows 终端** —— 按 `pwsh` > `powershell` 顺序探测目标 shell(`where.exe` 取完整路径;Windows 10+ 必带 Windows PowerShell,因此不再保留 cmd 兜底),再由隐藏 launcher 用 `Start-Process -WorkingDirectory` 开新窗口。Windows 上控制台程序**绝不能**带 `detached: true`——`DETACHED_PROCESS` 会让它们静默退出(exit 0 但不执行任何命令)。
- **WSL 上的终端与 Code** —— 路径先经 `wslpath` 翻译再交给 Windows 侧(WSL 下裸 spawn `bash`/`code` 没有控制台,窗口不可见);探测名带 `.exe` 后缀以确保命中 Windows 侧二进制而非 WSL 内的 Linux PowerShell。Code 优先尝试 Linux 侧 `code` 脚本,使 WSL 远程保留原生 WSL 路径。
- **Windows Code** —— Node ≥ 20.12 拒绝直接 spawn `.cmd` shim(CVE-2024-27980 加固),因此 `code.cmd` 通过 PowerShell launcher 调用(`& 'code.cmd' 'dir'`——PowerShell 的 & 自行按 PATH 解析 code.cmd,插件因此从不探测 VS Code 是否安装)。
- **生命周期** —— launcher 执行完即退出,其打开的窗口天然脱离 dsh 进程树,dsh 重启不会连带关闭。
## 🐛 故障排查
所有决策都会以 `workspace-native-open` 前缀写入 harness 日志:
- 收到请求 / 执行动作(info)
- 探测结果、spawn 失败原因、launcher 退出码与 stderr(warn)
失败同时镜像到浏览器控制台(`[dsh-native-open] …`),并携带在端点 JSON 响应的 `error` 字段中。
- `403 not allowed from this host` —— 请求的 `Host` 头不在 loopback 白名单内;请通过白名单内域名访问页面,或修改 `src/index.ts` 中的 `LOOPBACK_HOSTNAMES` 后重新构建。
- `408 body timeout` —— 请求体停滞;端点 10 秒后断开连接(超过 64 KB 的请求体返回 `413`)。
## 📝 已知限制
- 菜单项通过 DOM 观察注入工作区菜单(依赖触发按钮的 `aria-label`、与 portal 菜单的点击因果配对,以及菜单的 CSS 变量);若点击时无法解析工作区路径,菜单项保持禁用(置灰)。
- 插件为纯宿主实现——无 `dsh.client` bundle,也无浏览器资源构建流程。
- 远程 / LAN 访问按设计不展示菜单项,端点同时拒绝白名单外的 `Host` 头;`dsh.localhost` 已内建放行——如需放行其他本地域名,修改 `src/index.ts` 中的 `LOOPBACK_HOSTNAMES` 后重新构建。
- WSL 上「在终端打开」会在翻译后的 `\\wsl$` 路径打开 Windows 控制台——与资源管理器动作相同的 Windows 桌面交接约定。
## 🛠 开发
```sh
# 修改 src/index.ts 后重新构建 loader 产物
bun run build
```
- `src/index.ts` —— 插件源码(经 `dsh.bundle.patch` 以 dsh bundle 方式加载)。
- `lib/index.js` —— 构建出的 ESM 产物,由 `package.json#main` 引用。
- `cordis.patch.yml` —— 插入 `workspace-native-open` 行的 bundle patch。
- `package.nls.zh.json` / `package.nls.en.json` —— 简体中文 / 英文翻译目录(菜单标签与页面匹配字面量),构建时内联进 `lib/index.js`。
## 📄 许可证
[MIT](LICENSE)
Install
dsh plugin --profile web add github:JoeyLearnsToCode/dsh-workspace-native-open
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-native-open 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.