dsh-plugin-file-actions 0.1.7-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.en-US.md +444 -0
- package/README.md +226 -0
- package/cordis.patch.yml +3 -0
- package/lib/client.js +1222 -0
- package/lib/index.js +753 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# dsh-plugin-file-actions
|
|
4
|
+
|
|
5
|
+
**在 DSH Web 界面的每张交付文件卡片上:复制路径、用编辑器打开、在终端里运行 —— macOS / Windows / Linux 全平台。**
|
|
6
|
+
|
|
7
|
+
简体中文 · [English](README.en-US.md)
|
|
8
|
+
|
|
9
|
+
[](./LICENSE)
|
|
10
|
+
[](#-已知限制)
|
|
11
|
+
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
已针对 `@deepseek-ai/dsh@0.1.7-alpha.2` 验证。
|
|
17
|
+
|
|
18
|
+
## 🎬 演示
|
|
19
|
+
|
|
20
|
+
**文件卡片下拉菜单(卡片上唯一的控件)** —— dsh 0.1.7 起卡片动作由官方 `deliverables.file.actions` 席位提供;插件接管该席位,把官方那几行(默认应用 + OS 关联应用 + 在文件管理器中显示)吸收进自己的菜单,再补上它独有的:在终端运行、在终端打开所在目录、复制路径
|
|
21
|
+
|
|
22
|
+
[](docs/file-actions.gif)
|
|
23
|
+
|
|
24
|
+
**会话内超链接右键菜单** —— 在消息里右键一个链接,按链接类型弹出菜单:文件 / `mailto:` / 网页 / git / svn
|
|
25
|
+
|
|
26
|
+
[](docs/link-actions.gif)
|
|
27
|
+
|
|
28
|
+
## ✨ 功能
|
|
29
|
+
|
|
30
|
+
一个双面 DeepSeek Harness 插件,扩展 Web 界面里**交付文件卡片**(会话收尾列出的文件列表)的下拉菜单:
|
|
31
|
+
|
|
32
|
+
- 📋 **复制相对路径** / **复制绝对路径** —— 一次点击(纯浏览器侧,全平台),置于菜单底部收尾。卡片菜单与右键菜单都有。
|
|
33
|
+
- 🚀 **用探测到的编辑器/IDE 打开该文件** —— VS Code、Cursor、Sublime Text、JetBrains 全家桶等,每项带真实应用图标。应用探测与启动复用官方 `open-in-app` 的解析器:macOS 查 `.app` bundle,Windows 查注册表(App Paths / 卸载记录 / `%ProgramFiles%` 扫描),Linux 查 PATH 与 desktop entry。**右键菜单提供**(卡片上由吸收来的 OS 关联列表覆盖)。
|
|
34
|
+
- 📂 **在文件管理器中打开所在文件夹** —— 访达(macOS)/ 文件资源管理器(Windows)/ 文件管理器(Linux),真实应用图标,走官方 `POST /open-in-app/open` 路由,与会话右上角下拉菜单完全一致。**右键菜单提供**(卡片上由吸收来的 reveal 行覆盖)。
|
|
35
|
+
- 📂 **用默认应用打开 / 用任意关联应用打开 / 在文件管理器中显示** —— 从插件接管的那枚官方卡片控件吸收来的行:行内点名默认应用、服务端桌面为该文件报告的全部关联应用(带桌面自己的真实图标、标出默认项),以及「显示文件位置」。
|
|
36
|
+
- ▶️ **在终端运行该文件** / **在终端打开所在目录** —— 跟随本机探测到的终端:macOS 的终端.app / Ghostty,Windows 的 Windows Terminal / Git Bash,Linux 的 GNOME Terminal / Konsole / Ghostty。卡片菜单与右键菜单都有 —— 这是官方卡片动作没有的能力。
|
|
37
|
+
- 🖱️ **会话消息里的链接右键可用** —— 右键点击消息中渲染的文件链接(文件提及 / markdown 文件链接,`title` 即路径),在光标处弹出菜单;工作目录取当前查看会话的 `cwd`,相对路径按它解析。左键的官方预览行为不受影响。触屏设备上长按同样可用(见下文「移动端」)。
|
|
38
|
+
- 🔗 **按链接类型区分的右键菜单** —— `mailto:` 提供**复制邮箱地址 / 写邮件**;http(s) 链接提供**复制链接 / 在内置浏览器打开 / 在浏览器打开**(部署带内置浏览器 tab 时才出现,打开动作走官方 `sidebarRight` 服务);git 仓库地址(`.git` 后缀、`git@host:path`、`git://`、`ssh://`,锚点或行内代码)提供**复制链接 / 克隆到…**;svn 地址(`svn://` 家族,行内代码)提供**复制链接 / 检出到…**。克隆/检出会先弹出官方目录选择器选父目录,再由 Host 以 argv 直传运行 `git clone` / `svn checkout`(无 shell,URL 先经严格校验——拒绝前导 `-`、空白与超长串,杜绝选项注入),目标目录取 URL 末段。
|
|
39
|
+
|
|
40
|
+
> [!NOTE]
|
|
41
|
+
> [!NOTE]
|
|
42
|
+
> dsh 0.1.7 起「用编辑器打开」与「在文件管理器中显示」由官方 `open-in-app` 控件负责 —— 官方按**操作系统真实文件关联**列出能打开该文件的全部应用(macOS 查 NSWorkspace、Windows 查注册表、Linux 查 desktop entry),带默认应用与真实图标,比插件的固定目录更准。插件的卡片单元格因此**接管该席位**(同 id、更低 priority),把这些行**吸收**进自己唯一的下拉菜单,而不是在第二个按钮旁边重复一遍:默认应用打开、该文件的 OS 关联应用列表、在文件管理器中显示都在这里重现,且全部经席位自带的那条授权路由派发。插件自己的贡献 —— 终端运行、终端打开目录、复制路径 —— 接在同一菜单里;插件的策划编辑器/文件管理器目录只在**右键链接菜单**里保留(那里没有官方替代)。
|
|
43
|
+
|
|
44
|
+
## 🧩 应用列表如何决定
|
|
45
|
+
|
|
46
|
+
与官方 `open-in-app` 机制对齐 —— **官方解析器 + 本机探测过滤**,零配置:
|
|
47
|
+
|
|
48
|
+
- Host 半边直接加载官方 `@deepseek-ai/dsh-host-open-in-app` 包的解析库(精确锁版本),用与官方完全相同的定位链在本机解析每个应用,再以文件路径为参数启动解析到的可执行文件(macOS `open -a <bundle> <文件>`,Windows/Linux 直接 spawn 解析到的 exe)。官方 catalog 新增应用或调整定位拼写时,随插件发版升级。
|
|
49
|
+
- 编辑器与终端做**双交集**:浏览器半边把官方探测结果(`GET /open-in-app/apps`)与插件 info 路由返回的**本机解析结果**(`available` 字段)相交 —— 只有官方验证过 **且** 插件自己解析成功 **且** 在白名单内的应用才会出现 —— 即使插件随附的解析库与宿主 dsh 的版本有差异,也不可能再现「菜单里有、点了 400」。新装应用在下次 `dsh web` 重启后出现,卸载后立即消失。
|
|
50
|
+
- 文件管理器项**只跟随官方探测**(macOS `finder` / Windows `explorer` / Linux `filemanager`,官方 catalog 菜单顺序的第一位):它的启动就是官方 `POST /open-in-app/open` 传文件所在目录 —— 与会话右上角 split 按钮完全同一个调用 —— 因此官方路由自身就是「菜单里有、点了就能用」的完整保证,无需插件解析交集。
|
|
51
|
+
- 图标来自官方图标路由(`GET /open-in-app/icon/<id>`),与会话右上角同一份真实应用图标(Windows 上从可执行文件提取);缺失时退回通用占位图形。
|
|
52
|
+
|
|
53
|
+
终端项有悬停二级菜单:**在终端运行该文件**(命令来自下文的扩展名映射,映射不到的扩展名会置灰)与**在终端打开所在目录**(转发给官方 `POST /open-in-app/open` 路由,传父目录)。
|
|
54
|
+
|
|
55
|
+
### 📱 移动端(触屏)
|
|
56
|
+
|
|
57
|
+
手机/平板(`pointer: coarse`)上有两处专门适配:
|
|
58
|
+
|
|
59
|
+
- **链接长按即菜单** —— 移动端没有右键:iOS 的长按从不触发 `contextmenu` 事件(原生行为是链接预览弹窗),插件在会话消息流里改用 **长按(约 0.5 秒)弹出同款菜单**,长按后抬起不会误触链接跳转。会话消息里的链接与行内代码同时关闭了 iOS 的预览弹窗与选择放大镜(`-webkit-touch-callout` / `user-select`,仅限对话流,带 `data-plugin` 标记的样式注入,随 HMR 卸载);Android 的原生 `contextmenu` 长按路径继续走原有逻辑,与长按去重。
|
|
60
|
+
- **终端二级菜单拍平** —— 官方 Menu 的二级菜单固定弹在父行右侧且不做视口收拢,390px 宽的手机上会整个弹到屏幕外(实测 x 362..540),触屏也没有 hover。触屏上终端动作直接拍平进顶层菜单:**在 {终端} 中运行该文件** / **在 {终端} 中打开所在目录**,每行点名终端,桌面端保持二级菜单不变。
|
|
61
|
+
|
|
62
|
+
## 📦 安装
|
|
63
|
+
|
|
64
|
+
### 前置条件
|
|
65
|
+
|
|
66
|
+
- **dsh** 可用 —— `dsh --version`,或下面所有命令前缀 `npx @deepseek-ai/dsh`
|
|
67
|
+
- PATH 中有 **pnpm**(dsh 插件管理器会调用它):`npm install -g pnpm`
|
|
68
|
+
|
|
69
|
+
### 1. 添加插件
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
# 本地目录安装(link: —— 源码改动直接生效)
|
|
73
|
+
# ⚠️ link: 安装前先在 checkout 里跑一次 npm install:
|
|
74
|
+
# 插件依赖从 checkout 自己的 node_modules 解析,缺了它们 dsh web 会启动报错
|
|
75
|
+
npm install # 在 checkout 里执行(npm / git 安装可跳过,pnpm 会自带依赖)
|
|
76
|
+
npx @deepseek-ai/dsh plugin --profile web add link:/absolute/path/to/dsh-plugin-file-actions -w
|
|
77
|
+
|
|
78
|
+
# 从 GitHub 安装
|
|
79
|
+
npx @deepseek-ai/dsh plugin --profile web add git+https://github.com/cholf5/dsh-plugin-file-actions.git -w
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 2. 重启并刷新
|
|
83
|
+
|
|
84
|
+
重启 `dsh web`,然后刷新浏览器页面(更新后硬刷新)。
|
|
85
|
+
|
|
86
|
+
### 3. 验证(可选,但推荐)
|
|
87
|
+
|
|
88
|
+
验证路由已注册要用 cookie —— 未认证的 401 对所有 `/api` 路径都会发生,不构成注册证据:
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
curl -s -c /tmp/dsh-cookies.txt "http://127.0.0.1:3080/?token=<启动 URL 里的 token>" -o /dev/null # 铸造会话 cookie(303)
|
|
92
|
+
curl -s -b /tmp/dsh-cookies.txt http://127.0.0.1:3080/api/file-actions/info # 返回 JSON = 已注册;404 "not found" = 未注册
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
<details>
|
|
96
|
+
<summary>没有 pnpm 也不想装?手动回退</summary>
|
|
97
|
+
|
|
98
|
+
```sh
|
|
99
|
+
git clone https://github.com/cholf5/dsh-plugin-file-actions.git ~/.dsh/profiles/web/node_modules/dsh-plugin-file-actions
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
然后编辑 `~/.dsh/profiles/web/cordis.patch.yml`,使顶层列表包含(这是文件的最终状态 —— 不要盲目在 `[]` 行后追加):
|
|
103
|
+
|
|
104
|
+
```yaml
|
|
105
|
+
- insert:
|
|
106
|
+
- id: file-actions
|
|
107
|
+
name: dsh-plugin-file-actions
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
运行中的 dsh 会热加载这一行(patch 文件监视);之后刷新浏览器。
|
|
111
|
+
|
|
112
|
+
</details>
|
|
113
|
+
|
|
114
|
+
<details>
|
|
115
|
+
<summary>更新 / 卸载</summary>
|
|
116
|
+
|
|
117
|
+
```sh
|
|
118
|
+
npx @deepseek-ai/dsh plugin --profile web update dsh-plugin-file-actions -w # 或 remove
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
之后重启 `dsh web`。
|
|
122
|
+
|
|
123
|
+
</details>
|
|
124
|
+
|
|
125
|
+
### 🩺 故障排查
|
|
126
|
+
|
|
127
|
+
| 症状 | 原因与修复 |
|
|
128
|
+
|---|---|
|
|
129
|
+
| `dsh: command not found` | npx-only 安装 —— 命令前缀 `npx @deepseek-ai/dsh` |
|
|
130
|
+
| `pnpm was not found`(exit 127) | `npm install -g pnpm`,或用上面的手动回退 |
|
|
131
|
+
| `ERR_PNPM_ADDING_TO_ROOT` | 丢了 `-w` 标志 |
|
|
132
|
+
| 装了但界面没变化 | 重启 `dsh web`(bundle 层不热加载),再刷新页面 |
|
|
133
|
+
| 扩展菜单一直不出现在卡片上 | 卡片菜单接管官方 `deliverables.file.actions` 席位(id `open-in-app`、`priority: -10`);若官方升级换了席位或官方单元格 id,或卡片 DOM 改了 title 位置,插件按「退化不可见」渲染为空。先看 DevTools Console 有无报错,再对照已知限制更新席位注册 |
|
|
134
|
+
| 卡片上出现两个下拉 | 插件的 `priority: -10` 不再影子掉官方单元格 —— 官方升级改了账本按 id 选单元格的规则。对照安装版 `dsh-client-ui-slots` 重新核实 `entriesOfSlot` 的「每个 id 取第一个存活注册者」 |
|
|
135
|
+
| 卡片菜单显示「无法获取应用列表」 | 对属主给的 `actionUrl` 的 GET 关联查询失败或载荷形态不符;默认应用与 reveal 行仍可用。先看 Network 里那条请求,再对照已知限制 |
|
|
136
|
+
| `dsh web` 启动日志报 `file-actions:` 开头的错误,或 `Cannot find package '@deepseek-ai/dsh-host-open-in-app'` | 官方依赖没装或解析不到 —— `link:` 安装先在 checkout 里 `npm install`;npm/git 安装用 `dsh plugin --profile web update dsh-plugin-file-actions -w` 重装 |
|
|
137
|
+
| 菜单里没有某个编辑器/终端 | 该应用未被「官方探测 + 插件解析」双重验证(检查官方探测结果里有没有它)—— 两个交集都通过才会出现。文件管理器(访达等)只看官方探测:官方探测里有它,右键菜单才会有 |
|
|
138
|
+
|
|
139
|
+
## ⚙️ 配置
|
|
140
|
+
|
|
141
|
+
Host 行接受:
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
- insert:
|
|
145
|
+
- id: file-actions
|
|
146
|
+
name: dsh-plugin-file-actions
|
|
147
|
+
config:
|
|
148
|
+
runCommands: # 扩展名(无点)→ 在带引号的文件路径前运行的命令
|
|
149
|
+
py: python3 # 默认值随平台不同:Windows 默认 python / cmd /c / powershell -File 等
|
|
150
|
+
sh: bash
|
|
151
|
+
js: node
|
|
152
|
+
ts: tsx
|
|
153
|
+
allowExecutableBit: true # 未映射扩展名但带可执行位的文件也提供「运行」
|
|
154
|
+
launchTimeoutMs: 10000 # 有界命令的截止时间,也是分离启动的观察窗口
|
|
155
|
+
cloneTimeoutMs: 120000 # 一次 git clone / svn checkout 的截止时间(网络操作,上限远高于启动观察)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
> [!WARNING]
|
|
159
|
+
> 在 profile 自己的 `cordis.patch.yml` 里覆盖 —— patch 行会整行替换目标的
|
|
160
|
+
> `config`(无深合并),需要把每个键都重写一遍。
|
|
161
|
+
|
|
162
|
+
## 🔍 工作原理
|
|
163
|
+
|
|
164
|
+
| 层 | 文件 | 运行环境 |
|
|
165
|
+
| --- | --- | --- |
|
|
166
|
+
| Host | `lib/index.js` | Node —— Cordis Loader |
|
|
167
|
+
| Client | `lib/client.js` | 浏览器 —— dsh 客户端模块系统 |
|
|
168
|
+
|
|
169
|
+
### Host —— `lib/index.js`
|
|
170
|
+
|
|
171
|
+
Cordis 行 `file-actions` 在共享的已认证 `/api` 通道上注册四个精确路由:
|
|
172
|
+
|
|
173
|
+
| 路由 | 行为 |
|
|
174
|
+
| --- | --- |
|
|
175
|
+
| `GET /api/file-actions/info` | 注册探测 —— 返回 JSON body 即插件已加载 |
|
|
176
|
+
| `POST /api/file-actions/launch` | 用官方解析器解析应用 → `launchResolved` 以文件为参数启动(missing-executable 时按官方语义重解析一次) |
|
|
177
|
+
| `POST /api/file-actions/run` | 按下表构建终端命令并分离启动 |
|
|
178
|
+
| `POST /api/file-actions/clone` | 校验仓库 URL(VCS 形态 + 选项注入筛查)→ argv 直传 `git clone` / `svn checkout` 到派生子目录 |
|
|
179
|
+
|
|
180
|
+
终端适配(全部走官方 launcher 分离启动,凭据清洗过的环境变量,终端窗口比 dsh 活得久):
|
|
181
|
+
|
|
182
|
+
| 终端 | 平台 | 运行方式 |
|
|
183
|
+
| --- | --- | --- |
|
|
184
|
+
| Terminal.app | macOS | AppleScript `do script "cd <目录> && <命令>"` |
|
|
185
|
+
| Ghostty | macOS / Linux | macOS `open -na Ghostty --args -e`;Linux `ghostty --working-directory=<目录> -e bash -c` |
|
|
186
|
+
| Windows Terminal | Windows | `wt -d <目录> cmd /k`,命令行经环境变量 `%FILE_ACTIONS_RUN_CMD%` 传入 —— token 无空白,不受 wt 命令行重排影响,cmd 执行时才展开 |
|
|
187
|
+
| Git Bash | Windows | `<Git>/usr/bin/mintty.exe -e <Git>/usr/bin/bash.exe -l -c "cd <目录> && <命令>; exec '<Git>/usr/bin/bash.exe' -l -i"`(shell 一律走绝对路径 —— 裸 `exec bash` 会经 Windows PATH 命中 WSL 的 `system32\bash.exe`;`CHERE_INVOKING=1` 防止登录 shell 跳回 HOME) |
|
|
188
|
+
| GNOME Terminal / Konsole | Linux | `--working-directory` / `--workdir` + `bash -c "<命令>; exec bash -i"` |
|
|
189
|
+
|
|
190
|
+
POSIX 终端在命令结束后保留交互 shell(对齐 Terminal.app 行为);每条路由先请求 composition 的 `connection` 服务做拒绝判定 —— 与官方 open-in-app 相同的信任围栏。
|
|
191
|
+
|
|
192
|
+
### Client —— `lib/client.js`
|
|
193
|
+
|
|
194
|
+
交付卡片菜单**接管官方控件**:`ctx.slots.inject('deliverables.file.actions', …)` 以官方单元格**自己的 id `open-in-app`** 在官方 `deliverables.file.actions` 席位上注册插件菜单,并给 **`priority: -10`**。槽位账本把 list 席位按 `(priority, order)` 升序排、**每个 id 只取第一个存活注册者**,而 `register()` 只在**同 id 同 priority** 时才报错——报错文案本身就是官方给出的影子手法("register at a different priority to shadow it (lowest renders)")。因此官方 `FileRouteAction` 不再渲染,卡片上只剩**一个**下拉。既然接管了席位,控件原本的长处也一并**吸收**进来而不是留给第二个按钮:默认应用打开(`onAction('open')`)、该文件的 OS 关联应用列表(`onAction('open', appId)`,带桌面自己的图标)、在文件管理器中显示(`onAction('reveal')`)——全部经席位自带的 `onAction` 派发,也就是走**属主自己的授权 `actionUrl`**,绝不走插件路由。关联列表来自 **GET `actionUrl`**(与官方单元格同一个调用),图标是内嵌的 PNG/SVG data URL,进 `<img src>` 前按官方 `NativeFileApplication` 契约校验;读取失败时默认应用行照常可用并显示官方的「无法获取应用列表」行,`onAction` 返回的失败码则落到插件自己的 error 行(不用官方 toast)。其后是插件独有的终端段,最底部是纯浏览器侧的复制项。`available: false`(服务端 Host 没有桌面——官方控件在该状态下本来就渲染为空)时吸收来的行整体消失;`pending` 与关联读取在途时这些行**原地置灰**而非消失。文件路径不走 React fiber:单元格渲染在卡片内部,挂载后经自身宿主元素 `closest('[data-presented-file]')` 读预览按钮的 `title`(即按工作目录解析过的路径);`cwd` 来自席位标准 props(`sessionId` + `useSessions`,取交付所属会话)。菜单保持 `side:'top'` 向上展开避开视口钳位;插件 info 在单元格挂载后才落地时,经共享状态订阅原地补全。若官方 DOM 漂移(读不到路径),单元格渲染为空,不残留死按钮。审查页单独的 `deliverables.review.file.actions` 席位**刻意不动**,仍由官方控件渲染。
|
|
195
|
+
|
|
196
|
+
右键菜单走纯事件委托:`document` 级 `contextmenu` 监听匹配官方 markdown 渲染的文件链接按钮(文件提及与 markdown 文件链接共享同一个 hash 类,路径在其 `title` 属性里;输入区的引用 chip 同类但带 `data-ref-chip`,已排除),命中即 `preventDefault` 并在光标处经 `Menu` 的 `getAnchorRect`(portal 模式)弹出菜单。触屏设备上由 `touchstart`/`touchmove`/`touchend` 组成的长按检测走同一条打开路径(见上文「移动端」)。当前会话的工作目录由一个占据官方 `conversation.session.header.utilities` 槽位的空单元格发布 —— 与官方 open-in-app 按钮同一席位、同一标准 props(`sessionId` + `useSessions`)。URL 菜单的可选能力(内置浏览器 tab、目录选择器)按官方插件的方式在 `exports.inject` 里声明 `remote` / `remote.directoryPicker` 后读取 —— 未声明的服务读取会触发 cordis 的 `cannot get property ... without inject` 守卫,让整个菜单渲染崩溃。
|
|
197
|
+
|
|
198
|
+
> [!IMPORTANT]
|
|
199
|
+
> 若卡片 DOM 漂移(读不到预览按钮 `title`),插件单元格渲染为空而不是留下死按钮 —— 与旧 fiber 探测相同的「退化为不可见」原则。
|
|
200
|
+
|
|
201
|
+
## 🚧 已知限制
|
|
202
|
+
|
|
203
|
+
- **应用目录表固定**,对齐官方 open-in-app 哲学:部署方无法从 cordis.yml 添加自己的编辑器;扩展表意味着同时扩展 Host 的 `EDITOR_IDS`/`TERMINALS`(或客户端的 `FILE_MANAGER_IDS`)与客户端字典。哪些应用出现完全由官方探测决定(例如官方 catalog 未给 Zed 声明 win32 定位,Windows 上就不会出现 Zed)。
|
|
204
|
+
- **运行命令按扩展名识别。** 扩展名未映射的文件按可执行性提供「运行」:POSIX 看可执行位(客户端看不到它),Windows 按扩展名推导(`.exe`/`.bat`/`.cmd`/`.com`,chmod 在 Windows 上无效果;`.bat`/`.cmd` 默认已映射到 `cmd /c`)。无扩展名文件在 Windows 上不提供「运行」。需要时配置 `runCommands`。
|
|
205
|
+
- **Windows Terminal 的运行命令经 cmd 解释。** 命令字符串由 `cmd /k` 执行,配置值里的 cmd 元字符会被展开;`.sh` 等脚本建议在 Git Bash 终端里运行(其命令在 MSYS bash 上下文中执行)。Git Bash 的「运行」依赖完整 Git for Windows 安装自带的 mintty。
|
|
206
|
+
- **官方依赖精确锁版本。** Host 通过包清单定位 `@deepseek-ai/dsh-host-open-in-app` 的 `lib/types/resolver.js`(已发布 tarball 内含,并按版本尝试多种布局),依赖精确锁定在 `0.1.6-alpha.2`、不随 `dsh plugin update` 漂移;宿主 dsh 自带另一份解析库,两份可能的差异由客户端的双交集(官方探测 ∩ 插件解析)兜底。若未来版本改动布局,插件在启动时以 `file-actions:` 开头的明确错误失败,不会静默退化。
|
|
207
|
+
- **卡片菜单依赖官方席位与卡片 DOM 形态。** 菜单以官方单元格自己的 id `open-in-app`、`priority: -10` 接管官方 `deliverables.file.actions` 席位,路径读自卡片预览按钮的 `title`;dsh 升级若改名席位、改动官方发布的单元格 id,或改动卡片 DOM(title 改存他处),卡片菜单会退化消失,更新席位注册与 `cardPathOf` 即可。吸收来的关联列表读自属主给的 `actionUrl`,该路由的载荷形态若变,这些行会退化为「无法获取应用列表」行(插件逐条校验而非直接信任)。卡片上的编辑器/文件管理器段由官方 OS 关联覆盖 —— 若官方实现在某平台查不到你装的编辑器(未关联扩展名),右键链接菜单里的插件策划目录仍是兜底。
|
|
208
|
+
- **右键菜单依赖官方文件链接的 DOM 形态。** 匹配条件是「`fileMention` hash 类 + `title` 即路径」的按钮;dsh 升级若改变 markdown 渲染(类名换名、路径改存他处),右键菜单会静默失效(普通右键原样保留),更新 `FILE_LINK_SELECTOR` 即可。侧边栏等非当前会话语境里的文件链接会按当前查看会话的 `cwd` 解析路径。
|
|
209
|
+
- **URL 菜单只认官方渲染出来的链接。** 官方 sanitizer 只放行 http/https/mailto,所以 `svn://`、`git@` 仅以行内代码形态被识别(整段文本恰为仓库地址);纯文本里裸写的 URL 没有可靠边界,不作为菜单目标。svn over http(s) 与普通网页无法区分,一律给 http 菜单。
|
|
210
|
+
- **克隆写入宿主文件系统,方向上与「在终端运行该文件」同级。** URL 来自聊天文本,Host 以 argv 直传并先行拒绝可解析为选项的输入(前导 `-`)、空白与超长串;私有仓库在无缓存凭据时快速失败(`GIT_TERMINAL_PROMPT=0`),不会挂起有界命令。
|
|
211
|
+
|
|
212
|
+
## 🛠️ 开发
|
|
213
|
+
|
|
214
|
+
```sh
|
|
215
|
+
npm install
|
|
216
|
+
node --test
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
`node --test` 自动发现并运行 `test/` 下全部三个测试文件。测试通过 seam 注入(resolver / launcher / runCommand / stat / platform),在任意开发机上确定性覆盖 win32 / linux / darwin 三套适配器 —— 包括只读 POSIX mode 的 execute-bit 回退(stat seam 伪造 mode,不依赖 chmod),另有一条真实加载官方解析库的集成测试。`client-sweep.test.mjs` 再用最小假 DOM 加迷你 hook 运行时驱动真实的 `apply()` 与卡片菜单单元格 —— 覆盖 stub `require` 抓不到的结构类 bug,例如席位注册必须**影子**掉官方 `open-in-app` 单元格(同 id、更低 priority)而不是在旁并排第二枚控件、卡片菜单必须把吸收来的官方行(默认应用 / 关联列表 / reveal)排在终端与复制段之前、吸收来的每一行都必须经席位的 `onAction` 派发并把失败码映射到插件的 error 行、关联列表必须读自属主给的 `actionUrl` 且在读取失败或载荷畸形时退化为「无法获取应用列表」行、路径必须读自卡片预览按钮的 `title`、官方 DOM 漂移时单元格必须渲染为空。
|
|
220
|
+
|
|
221
|
+
> [!TIP]
|
|
222
|
+
> 通过 `link:` 安装时,改动 `lib/client.js` 会热替换进运行中的 `dsh web`,无需重启;Host 半边的改动需要重启 —— 且 checkout 必须先 `npm install`(依赖从 checkout 的 `node_modules` 解析)。
|
|
223
|
+
|
|
224
|
+
## 📄 许可
|
|
225
|
+
|
|
226
|
+
[MIT](./LICENSE) © cholf5
|
package/cordis.patch.yml
ADDED