dsh-open-in-app
dsh web-UI plugin: open the current session's workspace folder with an installed app (Finder, Terminal, VS Code, Ghostty, Zed, ...) — icons included
- Stars
- 1
- Language
- JavaScript
- Created
- Aug 20, 2026
- Updated
- Aug 24, 2026
Introduction
dsh-open-in-app
English | 中文
面向 DeepSeek Harness(dsh)Web UI 的一个插件:在聊天窗口右上角提供一个按钮,用已安装的应用打开当前会话的工作区文件夹。菜单按终端模拟器和流行 IDE/编辑器白名单整理,分组为“终端应用”和“编辑器与 IDE”。
仓库:github.com/open-dsh-plugins/dsh-open-in-app
截图
open-in-app 控件位于会话头部工具行(聊天窗口右上角),“Session log”按钮旁边。文件夹按钮一键打开工作区——使用你上次为该工作区选择的应用;下拉箭头打开选择菜单,显示最近使用的应用、系统默认文件夹处理器,以及主机上白名单内的终端和编辑器——每一项都带有应用图标:

功能
- 在会话头部工具行(聊天窗口右上角)新增一个 Codex 风格的拆分控件,样式与**“Session log”头部按钮一致的胶囊**(32px 高、1px 边框、18px 圆角、悬停填充),两段之间用细线分隔:
- 文件夹按钮一键打开工作区——使用你上次为该工作区选择的应用(按
cwd记录在localStorage中,按钮上显示其图标),否则回退到默认编辑器(优先级最高的已安装编辑器——VS Code → Cursor → Windsurf → Zed → IntelliJ IDEA → …——然后是系统默认); - 下拉箭头打开菜单,包含置顶的**“最近使用”条目、一个“系统默认应用”条目(内置
host.openPath行为;macOS 上是 Finder),以及主机上已安装的白名单终端和编辑器,每一项都带有应用图标(从应用包中以 32px 读取,以data:URL 传递;无法解析图标的仅显示名称)。“系统默认应用”行显示默认文件夹处理器的真实图标**(macOS 是 Finder,Windows 是资源管理器,Linux 是xdg-mime默认文件管理器),无法解析时回退为文件夹图标。
- 文件夹按钮一键打开工作区——使用你上次为该工作区选择的应用(按
- 从菜单选择应用即可用该应用打开当前工作区文件夹:
| 平台 | 应用发现 | 打开命令 | 图标 |
|---|---|---|---|
| macOS | /Applications、/System/Applications(+ Utilities)、~/Applications | open -a "<app>" <path> | 通过 sips 将 bundle .icns → 32px PNG;无 icns 的 bundle 用 qlmanage 回退 |
| Windows | %ProgramFiles%、%ProgramFiles(x86)%、%LOCALAPPDATA%\Programs(顶层 *.exe) | cmd /c start "" <exe> <path> | 通过 PowerShell [System.Drawing.Icon]::ExtractAssociatedIcon 提取内嵌 exe 图标 |
| Linux | /usr/share/applications、/usr/local/share/applications、~/.local/share/applications(*.desktop) | gtk-launch <id> <path>(回退到 xdg-open) | freedesktop Icon= 值:绝对路径、hicolor 主题(128→16)、可缩放 svg、pixmaps |
白名单
匹配对应用名(macOS 的 .app 基名、Windows 的 .exe 基名、Linux 的 desktop Name)不区分大小写。只有已安装且匹配的应用会出现在菜单中。
终端应用 — Terminal、iTerm2、Ghostty、Warp、Alacritty、kitty、WezTerm、Hyper、Tabby、Rio、Contour、Foot、Tilix、Terminator、Konsole、GNOME Terminal、xterm、mintty、Windows Terminal、PowerShell、Cmder、ConEmu。
编辑器与 IDE — Visual Studio Code、Cursor、Windsurf、Zed、Xcode、Android Studio、IntelliJ IDEA、PyCharm、WebStorm、GoLand、CLion、PhpStorm、RubyMine、Rider、DataGrip、DataSpell、RustRover、Fleet、Aqua、Visual Studio、Eclipse、NetBeans、Sublime Text、Nova、BBEdit、TextMate、CodeRunner、MacVim、Neovide、Emacs、Lite XL、HBuilderX。
要增删条目,编辑 lib/apps.js 中的 WHITELIST 数组——每个条目为 { id, category, match, exact? },其中 id 是规范显示名(也是原生打开命令必须能找到的名称),match 别名做子串匹配,exact 别名做全名匹配(用于诸如 code 这类会误匹配 CodeRunner 的短名称)。Windows JetBrains 启动器(idea64、pycharm64 等)与 Code.exe 已通过别名覆盖。
架构
- 宿主端(
lib/index.js):一个 Cordis 插件,注册一个 Typert Remote 服务openInApp(通过 Typert Gateway 的 source-mode 回退发现——无需生成 TYPERT manifest):openInApp/listApps→{ apps, defaultIcon? }:白名单内已安装应用(含展示图标)以及默认文件夹处理器的图标openInApp/openWith(path, appId)→ 用指定应用打开文件夹openInApp/openDefault(path)→ 用系统默认方式打开文件夹openInApp/openDefaultEditor(path)→ 用默认编辑器打开文件夹(见lib/apps.js中的EDITOR_PRIORITY)
- 图标(
lib/icons.js):为每个应用以及系统默认文件夹处理器(Finder / Explorer /xdg-mime默认)解析一个data:image/*图标,按平台尽力而为(见上表),按源路径 + mtime 缓存,使重复打开菜单开销很低;失败时缓存为“无”,绝不报错。在 macOS 上,与应用同名的 icns 优先于文件类型 icns(Zed.app 中的Zed.icns优先于Document.icns)。枚举的source路径作为不可枚举属性(lib/apps.js)携带,因此 JSON 传输永远不会看到它。 - 客户端(
lib/client.js):一个dsh.clientweb 模块,通过ctx.remote.$mount(...)挂载 Remote 端点,并注册conversation.session.header.utilities条目open-in-app。挂载运行在声明了remote的嵌套插件 fiber 中:api-gateway 将每个命名空间注册为点分隔的 cordis 服务(remote.openInApp),一个既挂载又注入自身命名空间的 fiber 会导致加载器死锁。因此消费方通过文档化的非严格 store 访问(ctx.reflect.get("remote.openInApp", false))读取该命名空间。 - Bundle(
cordis.patch.yml):一行加载器记录激活宿主端;profile 工具将其作为 profile bundle 引入。
安装
安装已发布到 npm 的包(需该包已发布到 npm registry):
dsh plugin --profile web add dsh-open-in-app
或在包含本仓库本地克隆的目录下执行:
git clone https://github.com/open-dsh-plugins/dsh-open-in-app.git
cd dsh-open-in-app
dsh plugin --profile web add .
然后重启 web 应用(dsh web)——加载器与客户端模块图在启动时组合,新增插件没有热重载。重启后,文件夹按钮会出现在会话标题旁,菜单会显示已安装的应用。
安全说明
- Remote 端点不在特权方法列表中,因此它们与其余
/api面处于同一浏览器信任边界之后(仅限 loopback / 已配置的可信主机)。 - 打开路径会在宿主机上派生原生进程。文件夹路径来自会话自身的
cwd,即该 agent 正在操作的目录。 - 图标解析仅读取枚举的应用路径(标准应用根目录)以及 freedesktop 图标主题目录;缓冲区上限为 256 KB,原生转换(
sips、qlmanage、PowerShell)带有可选的 abort 信号。 - 命令失败(
open退出码、应用缺失)会以菜单中的错误行呈现,而不是抛异常。
开发
lib/apps.js与lib/icons.js是纯 Node(无 dsh 导入)——可独立测试(node -e 'import("./lib/icons.js").then(m => m.listAppsWithIcons()).then(console.log)')。- 客户端 bundle 必须保持自包含:它只依赖平台种子词(
react、react/jsx-runtime、@deepseek-ai/dsh-client-ui-primitives)。 - 无需重建:没有构建步骤——
lib/原样发布。