dsh-open-in-dsh 0.0.0-stage → 0.2.0
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.md +221 -2
- package/assets/icon.ico +0 -0
- package/assets/icon.svg +12 -0
- package/client/index.js +692 -0
- package/cordis.patch.yml +29 -0
- package/docs/DETAIL.md +14 -0
- package/docs/DETAIL.zh.md +14 -0
- package/lib/client.js +707 -0
- package/lib/index.js +1188 -0
- package/locale/en.json +19 -0
- package/locale/zh.json +19 -0
- package/package.json +80 -4
- package/scripts/build-client.mjs +81 -0
- package/scripts/install-menu.ps1 +220 -0
- package/scripts/macos/install-macos.sh +432 -0
- package/scripts/macos/open-in-dsh +300 -0
- package/scripts/open-in-dsh.ps1 +339 -0
- package/scripts/open-in-dsh.vbs +80 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Cooper Zhuang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,222 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dsh-open-in-dsh
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
在 Windows 资源管理器 / macOS Finder 里**右键一个文件夹** →「**通过 DSH 打开**」,
|
|
4
|
+
DSH 就把这个目录作为工作区切入,并进到里面的一个空白会话。
|
|
5
|
+
|
|
6
|
+
- 已经登记过的工作区 → 直接切过去
|
|
7
|
+
- 没登记过 → 自动新建对应工作区
|
|
8
|
+
- **复用**该工作区已有的空白会话(不会每点一次就堆一个空会话)
|
|
9
|
+
- 不闪控制台窗口,也不多开浏览器
|
|
10
|
+
- **桌面版**:DSH 没开就把它启动起来;开着但被挡住/缩在托盘里就把它提到最前
|
|
11
|
+
|
|
12
|
+
反方向也有入口:DSH 里的**文件/目录右键菜单**提供「用 QSpace Pro / Finder / VS Code /
|
|
13
|
+
Warp / Terminal 打开」(Windows 是资源管理器),见下文「DSH 右键菜单里的『打开方式』」。
|
|
14
|
+
|
|
15
|
+
## 平台要求
|
|
16
|
+
|
|
17
|
+
| 平台 | 系统集成 | 需要什么 |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| Windows 10/11 | 资源管理器右键动词(文件夹 / 文件夹空白处) | `wscript.exe` + PowerShell(安装脚本用 `pwsh` 运行;动词运行时优先 `pwsh`,没有则回退 Windows PowerShell)。写 HKCU,不需要管理员权限 |
|
|
20
|
+
| macOS | Finder **快速操作** + 终端命令 `dsh-open` | 系统自带 Automator 服务、`curl`、`osascript`;可写入 `~/Library/Services` 与 `~/bin` |
|
|
21
|
+
| macOS(可选) | QSpace Pro 的「服务」子菜单 | 只有装了 QSpace Pro 才有意义,安装器会自动检测,也可用 `--qspace` 控制 |
|
|
22
|
+
|
|
23
|
+
DSH 侧:桌面版或 `dsh web` 都可以;声明的内核区间见「兼容性」。
|
|
24
|
+
|
|
25
|
+
## 安装
|
|
26
|
+
|
|
27
|
+
### 1. 插件(宿主 + 浏览器两半边)
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
dsh plugin --profile <profile> add dsh-open-in-dsh
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
把 `<profile>` 换成目标 profile(桌面版 `desktop`、`dsh web` 用 `web`)。
|
|
34
|
+
从源码安装时把最后一项换成 checkout 里的 `packages/dsh-open-in-dsh` 路径。
|
|
35
|
+
|
|
36
|
+
装完刷新页面;**改宿主半边(`lib/index.js`)后必须重启宿主**——桌面版要完全退出应用再打开
|
|
37
|
+
(关窗口只是缩进托盘),否则运行中的进程还在用模块缓存里的旧代码。
|
|
38
|
+
|
|
39
|
+
### 2. 注册系统右键菜单
|
|
40
|
+
|
|
41
|
+
菜单脚本装在插件包目录里(用 `dsh plugin add` 安装时是
|
|
42
|
+
`~/.dsh/profiles/<profile>/node_modules/dsh-open-in-dsh/scripts/`;源码安装则在 checkout 内)。
|
|
43
|
+
**装插件本身不会改系统菜单**,需要跑一次安装脚本:
|
|
44
|
+
|
|
45
|
+
**Windows**
|
|
46
|
+
|
|
47
|
+
```powershell
|
|
48
|
+
pwsh -File "$env:USERPROFILE\.dsh\profiles\<profile>\node_modules\dsh-open-in-dsh\scripts\install-menu.ps1" -Action install
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
- `-Action status`:只查看当前注册与解析结果;`-Action uninstall`:移除。
|
|
52
|
+
- 不带 `-Port` / `-App` 就是自动:检测到桌面版就注册桌面版端口与应用,否则退回 `dsh web` + 浏览器 URL。
|
|
53
|
+
也可以显式钉住:`-Port 3080 -App <exe 路径>`。
|
|
54
|
+
- 写入两个 HKCU 动词:`Directory\shell\DSHOpen`(右键文件夹)与
|
|
55
|
+
`Directory\Background\shell\DSHOpen`(右键文件夹空白处)。
|
|
56
|
+
- **装完重启一次资源管理器**——Explorer 按进程缓存右键菜单,不重启看不到新条目。
|
|
57
|
+
|
|
58
|
+
**macOS**
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
sh ~/.dsh/profiles/<profile>/node_modules/dsh-open-in-dsh/scripts/macos/install-macos.sh install
|
|
62
|
+
# 查看状态:… status 卸载:… uninstall
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
安装后会得到:
|
|
66
|
+
|
|
67
|
+
| 位置 | 是什么 |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `~/Library/Services/通过 DSH 打开.workflow` | Finder 快速操作,只收文件夹;右键文件夹 → **快速操作** → 通过 DSH 打开,支持多选 |
|
|
70
|
+
| `~/Library/Services/通过 DSH 打开 (QSpace).workflow` | 同一个标签/脚本,作用域是 QSpace Pro(装了才生成) |
|
|
71
|
+
| `~/bin/dsh-open` | 终端命令;不带参数就是当前目录 |
|
|
72
|
+
|
|
73
|
+
第一次安装后如果菜单没出现:`killall Finder` 或注销重登;菜单项/快捷键在
|
|
74
|
+
系统设置 → 键盘 → 键盘快捷键 → 服务。
|
|
75
|
+
|
|
76
|
+
### 卸载
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
# 1) 系统菜单
|
|
80
|
+
pwsh -File …/scripts/install-menu.ps1 -Action uninstall # Windows
|
|
81
|
+
sh …/scripts/macos/install-macos.sh uninstall # macOS(同时删 ~/bin/dsh-open)
|
|
82
|
+
# 2) 插件
|
|
83
|
+
dsh plugin --profile <profile> remove dsh-open-in-dsh
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
之后重启一次宿主。
|
|
87
|
+
|
|
88
|
+
## 配置
|
|
89
|
+
|
|
90
|
+
bundle 自带的 `cordis.patch.yml` 只是默认值;覆盖写在 profile 的 `cordis.patch.yml`
|
|
91
|
+
(或 `$DSH_HOME/settings.yaml` 的同名段),**整行替换 config,所以要写全**:
|
|
92
|
+
|
|
93
|
+
```yaml
|
|
94
|
+
- id: open-in-dsh
|
|
95
|
+
config:
|
|
96
|
+
enabled: true
|
|
97
|
+
alwaysNewSession: false # 复用已存在的空白会话
|
|
98
|
+
allowNonLoopback: false # 只接受回环来源
|
|
99
|
+
backlogTtlMs: 60000 # 冷启动补收的时间窗
|
|
100
|
+
socketPath: /path/to/run/dsh-open-in-dsh.sock # macOS 交接套接字;空串 = 不监听
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
| 字段 | 默认 | 说明 |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| `enabled` | `true` | 关掉后连路由都不注册 |
|
|
106
|
+
| `alwaysNewSession` | `false` | `true` = 每次右键都新建空白会话(同一文件夹点几次就堆几个,不建议) |
|
|
107
|
+
| `allowNonLoopback` | `false` | 是否接受非回环来源;这条路由等同于代码执行入口,不要轻易打开 |
|
|
108
|
+
| `backlogTtlMs` | `60000` | 页面没开着时,请求在这段时间内仍可被补收 |
|
|
109
|
+
| `socketPath` | `~/.dsh/run/dsh-open-in-dsh.sock` | macOS 交接套接字路径;空串表示不监听 |
|
|
110
|
+
|
|
111
|
+
`Config` schema 的作用是让这一行的 `config` 在加载时**被校验并补上默认值**,并让
|
|
112
|
+
`Config.listConfigs` 读得到键名;它不会长出一个可编辑表单。「设置 → 插件」是只读清单,
|
|
113
|
+
开关插件请用侧边栏「插件」页。
|
|
114
|
+
|
|
115
|
+
## 工作方式
|
|
116
|
+
|
|
117
|
+
### Windows
|
|
118
|
+
|
|
119
|
+
右键动词是独立进程,浏览器的页面拿不到「用户刚右键了哪个目录」,所以两边通过宿主碰头:
|
|
120
|
+
|
|
121
|
+
| 步骤 | 谁 | 做什么 |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| 1 | 菜单动词 | `open-in-dsh.vbs` 用环境变量把 `%V` 交给 `open-in-dsh.ps1`,后者解析端口/应用并 POST 到 `127.0.0.1:<port>/dsh-open-in-dsh/api/v1/open`;没人应答就先把桌面版启动起来再重投 |
|
|
124
|
+
| 2 | 宿主半边 | 校验路径(必须存在且是目录、只接受回环来源),入队并广播 |
|
|
125
|
+
| 3 | 页面 | 通过 SSE 收到请求,走客户端自己的 `workspaces.create({ path })` + `uiWorkspace.openWorkspace(id)` 完成跳转 |
|
|
126
|
+
| 4 | 启动器 | 桌面版:再启动一次应用把窗口提到最前(单实例锁 → `second-instance` → 聚焦);`dsh web`:没人跟随时打开浏览器 URL |
|
|
127
|
+
|
|
128
|
+
动词指向 `wscript.exe` 而不是 PowerShell,路径通过进程环境变量传:
|
|
129
|
+
GUI 子系统的 wscript 不会创建控制台窗口,因此右键不会闪窗;`%V` 里的空格、单引号、
|
|
130
|
+
盘符根目录的结尾反斜杠也不经过引号转义。
|
|
131
|
+
|
|
132
|
+
### macOS
|
|
133
|
+
|
|
134
|
+
桌面外壳从 2.0.19 起不再接受 `--dsh-desktop-workspace` 启动参数(Chromium 选项解析会直接
|
|
135
|
+
`bad option` 退出),所以快速操作走**插件自己的 Unix 域套接字**
|
|
136
|
+
(默认 `~/.dsh/run/dsh-open-in-dsh.sock`,权限 0600):启动器把同样的 JSON 打到套接字上,
|
|
137
|
+
与 Windows 那条 HTTP 路由共用同一个 handler。macOS 桌面版端口不固定(默认 43120,被占用
|
|
138
|
+
自动 +1),而且外壳在进路由前就把非 Electron renderer 的请求拒成 403,所以这条链路只能走
|
|
139
|
+
套接字,不能走浏览器 URL。
|
|
140
|
+
|
|
141
|
+
启动器(`scripts/macos/open-in-dsh`)把路径规范化(软链、相对路径、结尾斜杠、空格与引号)→
|
|
142
|
+
找 app(先看正在跑的那个,再看 `/Applications`,最后按 bundle id 走 Spotlight):
|
|
143
|
+
|
|
144
|
+
- **已经开着**:`curl --unix-socket <socket>` 投路径,然后不带参数再执行一次 app 可执行文件
|
|
145
|
+
把窗口提到最前;
|
|
146
|
+
- **没开**:`open -a "<app>"` 起应用,等宿主把套接字绑上(默认最多 30 s,`DSH_OPEN_WAIT` 可改)
|
|
147
|
+
再投递;
|
|
148
|
+
- **没装桌面版**:回落到同一条路由的 TCP 版(`dsh web`,默认 3080;`--port` 或 `DSH_OPEN_PORT` 可改)。
|
|
149
|
+
|
|
150
|
+
### 五条路由
|
|
151
|
+
|
|
152
|
+
| 路由 | 用途 |
|
|
153
|
+
|---|---|
|
|
154
|
+
| `POST /open` | 启动器提交目录;返回 `{seq, clients, recent}` |
|
|
155
|
+
| `GET /events` | 页面跟随请求(SSE,带 `hello` 与回放) |
|
|
156
|
+
| `GET /status` | 只报「有几个页面在跟」,无副作用 |
|
|
157
|
+
| `GET /apps` | 菜单先问本平台有哪些白名单 app 真的装了;返回 `{platform, apps}` |
|
|
158
|
+
| `POST /open-with` | 菜单把已存在的文件/目录交给白名单 app;返回 `{ok, app, path, directory}` |
|
|
159
|
+
|
|
160
|
+
### DSH 右键菜单里的「打开方式」
|
|
161
|
+
|
|
162
|
+
文件/目录的右键菜单通过内核的 `@deepseek-ai/dsh-client-ui-context-menu` 注册(客户端
|
|
163
|
+
`dsh.client.inject` 声明依赖),点开后按宿主报告的可用 app 列动作:
|
|
164
|
+
|
|
165
|
+
| 平台 | 动作 |
|
|
166
|
+
|---|---|
|
|
167
|
+
| macOS | QSpace Pro、Finder、VS Code、Warp、Terminal(装了才出现) |
|
|
168
|
+
| Windows | 资源管理器(目录直接打开,文件用 `/select,` 定位) |
|
|
169
|
+
|
|
170
|
+
### 为什么用 SSE 而不是轮询
|
|
171
|
+
|
|
172
|
+
Chrome 会把后台页面的定时器节流到每分钟一次,而刚点完右键时 DSH 窗口正好被资源管理器盖住
|
|
173
|
+
(就是后台页面)。SSE 是网络事件,后台窗口照样立刻收到。
|
|
174
|
+
|
|
175
|
+
## 安全边界
|
|
176
|
+
|
|
177
|
+
这条路由的作用是**让浏览器把任意本地目录当成工作区打开**,而工作区里会跑命令,
|
|
178
|
+
所以它等同于一个代码执行入口:
|
|
179
|
+
|
|
180
|
+
- **只接受回环来源**。内核 0.2.1 起 webserver 不再接受通配地址(`0.0.0.0` / `::` 加载即报错),
|
|
181
|
+
但仍可以钉到某个具体接口地址(比如本机的局域网 IP),那依旧是网络可达的——回环检查不能省。
|
|
182
|
+
- **带 `Origin` 的请求必须同源**。另一个网页即使能连到端口,也不能在背后点火。
|
|
183
|
+
- 启动器(wscript / PowerShell)不带 `Origin`,只靠回环这一条通过。
|
|
184
|
+
- 「打开方式」的 app 必须是白名单 id,路径必须是**已存在**的绝对路径;命令由白名单表拼成
|
|
185
|
+
argv、用 `execFile` 直接执行,不经过 shell。
|
|
186
|
+
|
|
187
|
+
## 已知限制
|
|
188
|
+
|
|
189
|
+
- 只注册了「文件夹」和「文件夹空白处」。右键**盘符**(如 `D:\`)需要再注册
|
|
190
|
+
`Drive\shell\DSHOpen`,路径规范化已支持盘符根目录。
|
|
191
|
+
- 桌面版提窗口靠「再启动一次应用」(每次多一个立即退出的短进程)。能不能真的抢到前台取决于
|
|
192
|
+
Windows 的前台权限策略;应用没开时走冷启动,窗口必然起来。
|
|
193
|
+
- 桌面版**登录页**出现时页面还没跑起来:请求会待在回放窗口里,登录进去、页面加载后才落地,
|
|
194
|
+
超过 `backlogTtlMs` 就过期,需要重按一次。
|
|
195
|
+
- 宿主半边改代码要重启宿主才生效;桌面版必须整个退出再打开。
|
|
196
|
+
- macOS:快速操作在**子菜单**里,不是顶层条目(顶层需要 Finder Sync 扩展 + Xcode 工程 + 签名);
|
|
197
|
+
右键文件夹空白处没有入口(服务总要有选中项);移动 checkout 后启动器绝对路径会失效,
|
|
198
|
+
重跑一次 `install-macos.sh install`——`status` 会打印 `STALE` 并以非 0 退出。
|
|
199
|
+
- 宿主重启后插件重新绑定套接字/路由;套接字不在时启动器明确报错并写日志,不会假装成功。
|
|
200
|
+
|
|
201
|
+
## 验证(开发)
|
|
202
|
+
|
|
203
|
+
```sh
|
|
204
|
+
npm test # 82 个测试(含客户端构建与语法检查)
|
|
205
|
+
node --test test/macos.test.mjs # macOS 启动器/安装器 17 个测试
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
`npm test` 覆盖宿主路由与回放、`%V` 的各种形态、脚本镜像(路由路径/端口/`-Probe` 一致)、
|
|
209
|
+
白名单与「打开方式」策略、会话定位,以及真实 `node:http` 上的集成路由(含跨域拒绝)。
|
|
210
|
+
|
|
211
|
+
## 兼容性
|
|
212
|
+
|
|
213
|
+
- **DSH**:`engines.dsh: ^0.2.1-rc.2 || ^0.2.2-alpha.2`;Node.js `>=24`。
|
|
214
|
+
- **会话存储**:宿主半边通过 `dsh-session-ops/store` 读会话头(peer 依赖,`dsh-session-ops >= 0.1.0`),
|
|
215
|
+
与 `dsh-session-ops` 共用同一份存储格式。
|
|
216
|
+
- 本插件不 import 任何内核包,只使用内核提供的服务(`webServer`、`workspaces`、`sessions`、
|
|
217
|
+
`uiWorkspace`、客户端模块系统);接口变化时最坏结果是某个入口失效,不会影响 DSH 本身。
|
|
218
|
+
- DSH 侧「打开方式」菜单依赖内核的 `@deepseek-ai/dsh-client-ui-context-menu` 客户端包。
|
|
219
|
+
|
|
220
|
+
## License
|
|
221
|
+
|
|
222
|
+
MIT
|
package/assets/icon.ico
ADDED
|
Binary file
|
package/assets/icon.svg
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="256" height="256" role="img" aria-label="DSH">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="dsh-open-in-dsh-bg" x1="0" y1="0" x2="1" y2="1">
|
|
4
|
+
<stop offset="0" stop-color="#6080ff"/>
|
|
5
|
+
<stop offset="1" stop-color="#263cc8"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<rect x="16" y="16" width="224" height="224" fill="url(#dsh-open-in-dsh-bg)"/>
|
|
9
|
+
<text x="128" y="155" text-anchor="middle"
|
|
10
|
+
font-family="Segoe UI, Helvetica, Arial, sans-serif"
|
|
11
|
+
font-size="78" font-weight="700" fill="#ffffff">DSH</text>
|
|
12
|
+
</svg>
|