@winking-r/dsh-uniterminal 0.2.16 → 0.2.18

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/CHANGELOG.md ADDED
@@ -0,0 +1,73 @@
1
+ # 更新日志
2
+
3
+ 遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
4
+
5
+ `0.x` 阶段:**次要版本可能包含不兼容变更**(配置项与插件 API 仍可能调整),补丁版本只修缺陷。
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.2.18] - 2026-10-29
10
+
11
+ 纯文档版本,**代码与 0.2.17 完全相同**。
12
+
13
+ ### 文档
14
+
15
+ - README 新增**截图**(主机列表 / 终端 / 连接表单 / SFTP / 外观):演示数据为 `demo01`–`demo03` 与 RFC 5737 文档段 `192.0.2.0/24`;
16
+ 图片经绝对 URL 引用,npm 页面同样能显示。
17
+ - 安装说明改为**一条命令**:`dsh plugin --profile <profile 名> add @winking-r/dsh-uniterminal@latest`;
18
+ 手工方式(`pnpm add` + 编辑 `dsh.profile.bundles`)作为备选保留。
19
+ - **已知限制去掉开发日志式条目**:划掉的旧问题、「已解决(M4-x)」、里程碑编号、"已在真实服务器验证"这类记录都不再出现在用户文档里;
20
+ 只保留用户实际会遇到的行为,凭据细节统一链到 [docs/SECURITY.md](docs/SECURITY.md)。
21
+ - [SECURITY.md](docs/SECURITY.md) 同步去掉里程碑编号与历史叙述,改为陈述当前行为。
22
+ - 新增 [CONTRIBUTING.md](CONTRIBUTING.md):源码断言的约定、DOM 测试模板、CSS 契约。
23
+
24
+ ## [0.2.17] - 2026-10-29
25
+
26
+ ### 修复
27
+
28
+ - **行尾「更多操作」按钮的菜单被面板右边缘裁掉**:锚点靠右时菜单仍向右伸展,伸出面板后被宿主的
29
+ `overflow` 裁掉,「移入分组…」「测试连接」只显示半截。现在锚点过半宽时改为**右对齐、向左展开**;
30
+ 鼠标右键、`Shift+F10`/菜单键、「⋯」按钮三条入口共用同一套判定(`src/client/menu-anchor.ts`)。
31
+
32
+ ### 文档
33
+
34
+ - 新增 `CONTRIBUTING.md`:源码断言的约定(改 CSS/文案要同步更新断言、不要删断言)、
35
+ DOM 测试模板(`// @vitest-environment jsdom` + `afterEach(cleanup)`)、CSS 契约、
36
+ 以及**禁止用固定字符数切源码**这条踩过两次的坑。
37
+ - README 补「右栏承载」一节:装了 `DSH-better-sidebar` 优先注册到 `ctx.betterSidebar`,
38
+ 否则回落官方 `sidebarRightTabs`。
39
+
40
+ ## [0.2.16] - 2026-10-29
41
+
42
+ 首个公开发布版本(MIT)。
43
+
44
+ ### 新增
45
+
46
+ - SSH 终端(xterm.js)、SFTP 文件管理、端口转发(`-L` / `-R` / `-D`)、跳板链、分组管理、外观设置。
47
+ - 一个侧边栏标签装多条会话,顶部会话栏切换;同一主机连两次显示 `#2`;刷新页面自动接回会话。
48
+ - **凭据交给操作系统密码箱**(Windows 凭据管理器 / macOS 钥匙串 / Linux Secret Service),
49
+ 插件自身不实现任何加密;只有机器没有系统密码箱时才回落明文文件(0600 + 原子写),并在界面如实说明。
50
+ - 失败提示区分 7 类原因(超时 / 拒绝 / DNS / 不可达 / 主机指纹 / 认证 / 未知)并给出下一步动作;
51
+ 内部字段名不再出现在界面上(细节只进 diag)。
52
+
53
+ ### 无障碍
54
+
55
+ - 右键菜单与确认弹框:打开即聚焦、方向键/Home/End、`Tab` 焦点陷阱、关闭后**把焦点还给触发元素**。
56
+ - 提示条改为常驻 live region(成功 `status/polite`、失败 `alert/assertive`),读屏能听见错误。
57
+ - SFTP 文件行支持键盘选中(`aria-pressed`);主机行「连接 / 测试」的可访问名带主机名。
58
+
59
+ ### 设计
60
+
61
+ - 引入 token 层(字号 13/14/16 三级阶梯、圆角 4/6/10/999、灰阶与遮罩、语义色单一来源)。
62
+ - 内联样式收敛 66%(541 → 182 条声明);重复选择器 4 组 → 0 组。
63
+
64
+ ### 质量
65
+
66
+ - 严格 TypeScript(`strict` + `noUncheckedIndexedAccess`):`:any` 与 `@ts-ignore` 均为 0。
67
+ - 64 个测试文件 / 915 个用例(含 jsdom 真实交互用例);`pnpm audit --prod` 零已知漏洞。
68
+ - `prepublishOnly` 强制跑 typecheck + test + build,过不了发不出去。
69
+
70
+ [Unreleased]: https://github.com/WinKing-R/dsh-uniterminal/compare/v0.2.18...HEAD
71
+ [0.2.18]: https://github.com/WinKing-R/dsh-uniterminal/compare/v0.2.17...v0.2.18
72
+ [0.2.17]: https://github.com/WinKing-R/dsh-uniterminal/compare/v0.2.16...v0.2.17
73
+ [0.2.16]: https://github.com/WinKing-R/dsh-uniterminal/releases/tag/v0.2.16
package/README.md CHANGED
@@ -1,13 +1,30 @@
1
1
  # UniTerminal · DSH 远程终端插件
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/@winking-r/dsh-uniterminal.svg)](https://www.npmjs.com/package/@winking-r/dsh-uniterminal)
4
+ [![license](https://img.shields.io/npm/l/@winking-r/dsh-uniterminal.svg)](LICENSE)
5
+ [![GitHub](https://img.shields.io/badge/GitHub-WinKing--R%2Fdsh--uniterminal-blue)](https://github.com/WinKing-R/dsh-uniterminal)
6
+
3
7
  SSH / SFTP / 端口转发,挂在 DSH 右侧栏里。**一个侧边栏标签里装多条会话**(顶部会话栏切换;同一台主机连两次显示 `#2`),
4
8
  每条会话的终端、文件、隧道三视图并列。
5
9
 
10
+ ## 截图
11
+
12
+ | 主机列表:分组 · 搜索 · 行内「测试 / 连接」,行尾 `⋯` 是其余操作 | 终端:一个侧边栏标签装多条会话,顶部会话栏切换(同一主机连两次显示 `#2`) |
13
+ |---|---|
14
+ | <img src="https://raw.githubusercontent.com/WinKing-R/dsh-uniterminal/main/docs/images/hosts.png" width="400"> | <img src="https://raw.githubusercontent.com/WinKing-R/dsh-uniterminal/main/docs/images/terminal.png" width="400"> |
15
+
16
+ | 连接表单:记住的密码交给**操作系统密码箱** | SFTP:目录浏览、上传下载、进度 | 外观:字体、字号、行高、主题模式 |
17
+ |---|---|---|
18
+ | <img src="https://raw.githubusercontent.com/WinKing-R/dsh-uniterminal/main/docs/images/connect-form.png" width="260"> | <img src="https://raw.githubusercontent.com/WinKing-R/dsh-uniterminal/main/docs/images/sftp.png" width="260"> | <img src="https://raw.githubusercontent.com/WinKing-R/dsh-uniterminal/main/docs/images/appearance.png" width="260"> |
19
+
20
+ > 截图使用演示数据(主机名 `demo01`–`demo03`,地址为 [RFC 5737](https://www.rfc-editor.org/rfc/rfc5737) 文档保留段 `192.0.2.0/24`)。
21
+ > 图片用**绝对 URL** 引用:npm 页面不解析相对路径,相对写法在 npmjs.com 上会显示破图。
22
+
6
23
  ## 状态
7
24
 
8
25
  | 项 | 值 |
9
26
  |---|---|
10
- | 版本 | `0.2.16`(`0.x`:API 与配置项仍可能变,按语义化版本升号) |
27
+ | 版本 | `0.2.18`(`0.x`:API 与配置项仍可能变,按语义化版本升号;变更见 [CHANGELOG](CHANGELOG.md)) |
11
28
  | 许可证 | **MIT**(见 [LICENSE](LICENSE))——可自由使用、修改、商用,保留版权声明即可 |
12
29
  | 环境 | DSH `>=0.2.1-rc.2`、Node `>=20` |
13
30
 
@@ -23,17 +40,33 @@ SSH / SFTP / 端口转发,挂在 DSH 右侧栏里。**一个侧边栏标签里
23
40
  ## 安装
24
41
 
25
42
  前提:DSH `>=0.2.1-rc.2`(见本包 `package.json` 的 `dsh.engines`)、Node `>=20`。
26
- 从源码装还需要 pnpm;用打好的 `tgz` 安装则**不需要**自己构建。
43
+
44
+ ### 一条命令(推荐)
45
+
46
+ DSH 自带 CLI 会把「装包 + 登记到 profile」一步做完:
47
+
48
+ ```bash
49
+ dsh plugin --profile desktop add @winking-r/dsh-uniterminal@latest
50
+ ```
51
+
52
+ `--profile` 填你的 profile 名 —— `~/.dsh/profiles/` 下有哪些目录就用哪个(常见 `desktop` 或 `web`)。
53
+ 装完重启 DSH,右栏会出现 `UniTerminal` 标签。
54
+
55
+ > 没装 `dsh` 命令,或者要装本地构建(`link:` / `tgz`)时,用下面的手动方式,效果等价。
56
+
57
+ ### 手动方式(等价,两步)
27
58
 
28
59
  ```bash
29
- # 在 DSH 的 desktop profile 目录下执行
30
60
  cd ~/.dsh/profiles/desktop
31
61
 
32
- # 方式一:从源码链接(改代码即生效,开发用)
33
- pnpm add "link:<你的本仓库绝对路径>"
62
+ # npm 上的正式包
63
+ pnpm add @winking-r/dsh-uniterminal
64
+
65
+ # 或者:改代码即生效(开发用)
66
+ pnpm add "link:<本仓库绝对路径>"
34
67
 
35
- # 方式二:从目录安装(用打包好的 tgz,无需 Node 工具链)
36
- pnpm add "file:<tgz 的绝对路径>"
68
+ # 或者:装打包好的 tgz(不需要自己构建)
69
+ pnpm add "file:<tgz 绝对路径>"
37
70
  ```
38
71
 
39
72
  然后在 `~/.dsh/profiles/desktop/package.json` 的 `dsh.profile.bundles` 数组里追加一行本包名:
@@ -44,6 +77,19 @@ pnpm add "file:<tgz 的绝对路径>"
44
77
 
45
78
  重启 DSH。若右栏没有自动出现本插件,用右栏的 `+` 手动添加一个 `UniTerminal` 标签。
46
79
 
80
+ ### 右栏承载:官方 `sidebarRight` 或第三方 `DSH-better-sidebar`
81
+
82
+ 本插件**双注册 + 运行时探测**(`src/client/right-pane.ts`):
83
+
84
+ | 你的 DSH 里装了 | 插件注册到 | 结果 |
85
+ |---|---|---|
86
+ | [`DSH-better-sidebar`](https://github.com/omdsh-dev/DSH-better-sidebar) | `ctx.betterSidebar`(**优先**) | 右栏像 IDE 那样多标签并存,本插件占一个标签 |
87
+ | 只有官方 `@deepseek-ai/dsh-client-ui-sidebar-right` | `ctx.sidebarRightTabs` | 回落到官方右栏标签 |
88
+
89
+ 两种都能用;**没有 better-sidebar 也能跑**(会自动回落)。
90
+ 若右栏始终不出现本插件,先确认这两者至少有一个已启用 —— 诊断见
91
+ [`docs/acceptance-m0.md`](docs/acceptance-m0.md)(`target=none` 表示两套注册表都不可用)。
92
+
47
93
  > **改动代码后必须重启 DSH**:宿主半边(`lib/index.js`)在启动时加载,硬刷新页面不会生效。
48
94
 
49
95
  ### 卸载
@@ -60,7 +106,7 @@ pnpm remove @winking-r/dsh-uniterminal
60
106
 
61
107
  | 域 | 能力 |
62
108
  |---|---|
63
- | 主机 | 添加 / 编辑 / 删除(二次确认)/ 分组;从 `~/.ssh/config` 导入;连接前「测试」连通性;**孤儿会话可接管或关闭** |
109
+ | 主机 | 添加 / 编辑 / 删除(二次确认)/ 分组;从 `~/.ssh/config` 导入;连接前「测试」连通性;**孤儿会话可接管或关闭**(接管不需要重新输入密码) |
64
110
  | 认证 | 密码、私钥(读本地路径或粘贴内容)、**两者可同时启用**;按主机「记住密码」;跳板链 `ProxyJump` |
65
111
  | 终端 | xterm.js,自适应尺寸,断线重连;面板重挂载与**页面刷新**后会话与输出不丢(存档落 `sessionStorage`) |
66
112
  | 文件 | SFTP 浏览、多选上传(带进度、可取消)、下载、新建目录、删除 |
@@ -104,16 +150,14 @@ pnpm remove @winking-r/dsh-uniterminal
104
150
  - **没有** → 回落明文文件 `credentials.json`,靠 `chmod 0600` 与 Windows 账户目录权限保护;Windows 上 `chmod` 实际无效,真实防护来自 NTFS ACL。**不要在多用户共用的机器上勾选「记住密码」。**
105
151
  - **密码箱这一刻用不了**(被锁 / 服务没起)→ 临时改用本机文件,界面会明说;此前存在密码箱里的条目暂时取不到。
106
152
  界面表单页常驻小字会说明当前用的是哪一种(详见 [docs/SECURITY.md](docs/SECURITY.md))。
107
- **不再提供插件自管的加密档位**:旧版那套「主密码 + 插件自管加密文件」已整体删除,既不提供也不支持。
108
- 本机若还留着旧版凭据文件,界面会提示「清理旧版凭据」。**清理只会丢弃旧格式文件里那份读不出来的数据——明文库里已记住的密码不受影响**;旧档里的那份密码需要重新输入一次。
153
+ 早期试用版若在本机留下旧格式凭据文件,界面会提示「清理旧版凭据」——**清理只删掉那份读不出来的旧数据,明文库里已记住的密码不受影响**。
109
154
  2. **不提供终端锁屏**(明确不做的决策,不是遗漏)。离开座位时已连接的会话没有保护——需要时请自行锁屏或断开连接。
110
- 3. **`-R` 远程转发**已在真实服务器(OpenSSH 9.9,`AllowTcpForwarding yes`)验证通路成立。仍未解决的是:**停止隧道不会立即释放服务端的远端端口**,要等会话关闭(ssh2 不提供 cancel-tcpip-forward);在此期间到达的连接会被明确拒绝,不会穿透。
155
+ 3. **`-R` 远程转发**可用,但**停止隧道不会立即释放服务端的远端端口**,要等会话关闭(ssh2 不提供 cancel-tcpip-forward);在此期间到达的连接会被明确拒绝,不会穿透。
111
156
  4. **跳板链每跳使用同一套凭据**(同一口令或同一私钥)。「跳板机用 A 钥、目标机用 B 钥」尚未支持。
112
157
  5. **覆盖式上传**优先用 OpenSSH 的 `posix-rename@openssh.com` 做原子替换;服务器不支持时会退化为「先删目标再改名」,存在极短窗口(目标暂时不存在)。
113
- 6. ~~没有会话列表 UI:刷新页面会丢失前端会话存档~~ → **已解决(M4-1)**:存档写入 `sessionStorage`,刷新后自动回到终端并重连;列表页会列出「未接管的会话」,可一键接管(无需重新输入密码)或关闭。
114
- 7. **SFTP 路径记忆、`-R` 端口即时释放**在待办中。终端锁屏已明确不做。
115
- 8. **shell 模式拒绝交互式/全屏程序**(vi/vim/nano/less/top/htop/watch/man/tmux/screen/ssh/su):它们会占满用户终端、提示符回不来。按分段首词判定,sudo vim、xargs vim 之类仍可绕过——真正的防线是审计日志。若将来需要让 AI 驱动 TUI,主流做法是塞进 detached tmux 会话(send-keys + capture-pane),届时需要在服务器安装 tmux。
116
- 9. **AI 命令执行的安全边界**:工具默认开启,但只能操作**你已打开的会话**——AI 无法自行建立连接或提交凭据。每次执行都会写入 `diag.log`(命令预览截断 120 字符并 scrub 口令/私钥)。如不需要,把 `agentExec` 设为 `off`。
158
+ 6. **SFTP 路径记忆、`-R` 端口即时释放**在待办中。
159
+ 7. **shell 模式拒绝交互式/全屏程序**(vi/vim/nano/less/top/htop/watch/man/tmux/screen/ssh/su):它们会占满用户终端、提示符回不来。按分段首词判定,sudo vim、xargs vim 之类仍可绕过——真正的防线是审计日志。
160
+ 8. **AI 命令执行的安全边界**:工具默认开启,但只能操作**你已打开的会话**——AI 无法自行建立连接或提交凭据。每次执行都会写入 `diag.log`(命令预览截断 120 字符并 scrub 口令/私钥)。如不需要,把 `agentExec` 设为 `off`。
117
161
 
118
162
  ## 结构
119
163
 
package/docs/SECURITY.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # 安全说明(UniTerminal 凭据存储)
2
2
 
3
3
  > 这一份是**凭据存储**的对外说明:威胁模型、边界、密码存在哪、怎么清、已知限制。
4
- > 相关台账:`docs/superpowers/plans/ledger-m0.md` 的 M65(删掉自研密码学)与 M66(接操作系统密码箱)。
4
+ > 设计与取舍的完整记录见 `docs/superpowers/plans/ledger-m0.md`。
5
5
 
6
6
  ## 一、威胁模型
7
7
 
@@ -42,9 +42,9 @@
42
42
  `secret` 的字段一律被路由层拒绝(`CREDENTIAL_FIELDS` 白名单)。
43
43
  3. **密码永远不进日志与诊断。**
44
44
  `diag.log` 与 `reportClient()` 上报里只有 id、状态、类别;写入失败时错误消息里的密码会被 `scrub` 抹掉。
45
- 4. **密码永远不落进插件自己的加密文件。**
46
- 插件**不实现任何加密**:上一批的自研密码学(主密码 + scrypt + AES-256-GCM)已整体删除,
47
- 那是一段"走不到"的攻击面。现在要么交给操作系统密码箱,要么明文文件(并如实说明)。
45
+ 4. **插件不自己实现加密。**
46
+ 密码只有两个去处:**操作系统密码箱**,或**明文文件**(并在界面如实说明当前是哪种)。
47
+ 不存在"插件自建加密、由插件保管密钥"这条路。
48
48
 
49
49
  ## 三、密码存在哪
50
50
 
@@ -109,6 +109,6 @@ Linux 上不显式指定后端时,底层库会"Secret Service → 内核 keyri
109
109
  5. **Linux 上没有 Secret Service**(无 GNOME Keyring / KWallet 的精简桌面)时,
110
110
  插件会走文件后端并明说——**不会**退到内核 keyring(不跨重启)。
111
111
  6. **没有终端锁屏**:离开座位时已建立的会话没有任何保护,需要时请自行锁屏或断开连接。
112
- 7. **插件不提供自研加密**(主密码 / 自建加密文件已整体删除,见台账 M65)。
112
+ 7. **插件不提供自研加密**:没有"插件保管密钥"这条路。
113
113
  需要更强的保护时,请用操作系统的全盘加密与账户隔离。
114
114
 
package/lib/client.js CHANGED
@@ -1404,6 +1404,7 @@ window.__ModuleLoader__.load({
1404
1404
  "aria-label": props.label,
1405
1405
  style: {
1406
1406
  left: props.left,
1407
+ right: props.right,
1407
1408
  top: props.top
1408
1409
  },
1409
1410
  onMouseDown: (event) => event.stopPropagation(),
@@ -1426,6 +1427,23 @@ window.__ModuleLoader__.load({
1426
1427
  }, item.label))
1427
1428
  });
1428
1429
  }
1430
+ // src/client/menu-anchor.ts
1431
+ /**
1432
+ * @param anchorX 锚点的横向位置(相对面板左边缘,像素)
1433
+ * @param anchorY 锚点的纵向位置(相对面板上边缘,像素)
1434
+ * @param paneWidth 面板宽度;0 或未知时退回左对齐(总比跑到面板外好)
1435
+ */
1436
+ function menuAnchor(anchorX, anchorY, paneWidth) {
1437
+ const top = Math.max(0, anchorY);
1438
+ if (paneWidth > 0 && anchorX > paneWidth / 2) return {
1439
+ right: Math.max(0, paneWidth - anchorX),
1440
+ top
1441
+ };
1442
+ return {
1443
+ left: Math.max(0, anchorX),
1444
+ top
1445
+ };
1446
+ }
1429
1447
  // src/client/host-filter.ts
1430
1448
  /**
1431
1449
  * 主机/分组搜索。
@@ -2270,11 +2288,9 @@ window.__ModuleLoader__.load({
2270
2288
  */
2271
2289
  function anchorOf(element) {
2272
2290
  const rect = element.getBoundingClientRect();
2273
- const pane = paneRef.current?.getBoundingClientRect();
2274
- return {
2275
- left: rect.left - (pane?.left ?? 0),
2276
- top: rect.bottom - (pane?.top ?? 0)
2277
- };
2291
+ const pane = paneRef.current;
2292
+ const paneRect = pane?.getBoundingClientRect();
2293
+ return menuAnchor(rect.right - (paneRect?.left ?? 0), rect.bottom - (paneRect?.top ?? 0), pane?.clientWidth ?? 0);
2278
2294
  }
2279
2295
  /** 打开主机菜单:trigger 既当锚点,也是关闭后焦点要回的元素。 */
2280
2296
  function openRowMenu(trigger, profileId) {
@@ -2316,10 +2332,10 @@ window.__ModuleLoader__.load({
2316
2332
  },
2317
2333
  onContextMenu: (event) => {
2318
2334
  event.preventDefault();
2319
- const rect = paneRef.current?.getBoundingClientRect();
2335
+ const pane = paneRef.current;
2336
+ const rect = pane?.getBoundingClientRect();
2320
2337
  setMenu({
2321
- left: event.clientX - (rect?.left ?? 0),
2322
- top: event.clientY - (rect?.top ?? 0),
2338
+ ...menuAnchor(event.clientX - (rect?.left ?? 0), event.clientY - (rect?.top ?? 0), pane?.clientWidth ?? 0),
2323
2339
  profileId: profile.id,
2324
2340
  trigger: event.currentTarget
2325
2341
  });
@@ -2754,10 +2770,10 @@ window.__ModuleLoader__.load({
2754
2770
  },
2755
2771
  onContextMenu: (event) => {
2756
2772
  event.preventDefault();
2757
- const rect = paneRef.current?.getBoundingClientRect();
2773
+ const pane = paneRef.current;
2774
+ const rect = pane?.getBoundingClientRect();
2758
2775
  setGroupMenu({
2759
- left: event.clientX - (rect?.left ?? 0),
2760
- top: event.clientY - (rect?.top ?? 0),
2776
+ ...menuAnchor(event.clientX - (rect?.left ?? 0), event.clientY - (rect?.top ?? 0), pane?.clientWidth ?? 0),
2761
2777
  groupId: node.id,
2762
2778
  trigger: event.currentTarget
2763
2779
  });
@@ -2904,6 +2920,7 @@ window.__ModuleLoader__.load({
2904
2920
  if (target === void 0) return null;
2905
2921
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextMenu, {
2906
2922
  left: menu.left,
2923
+ right: menu.right,
2907
2924
  top: menu.top,
2908
2925
  label: "主机 " + target.name + " 的操作",
2909
2926
  returnFocusTo: menu.trigger,
@@ -2937,6 +2954,7 @@ window.__ModuleLoader__.load({
2937
2954
  if (node === void 0) return null;
2938
2955
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextMenu, {
2939
2956
  left: groupMenu.left,
2957
+ right: groupMenu.right,
2940
2958
  top: groupMenu.top,
2941
2959
  label: "分组 " + node.path.join(" / ") + " 的操作",
2942
2960
  returnFocusTo: groupMenu.trigger,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@winking-r/dsh-uniterminal",
3
- "version": "0.2.16",
3
+ "version": "0.2.18",
4
4
  "description": "在 DSH 右侧栏里提供 SSH 终端、SFTP 文件管理与端口转发的插件;密码交给操作系统密码箱保管,插件自身不实现加密。",
5
5
  "keywords": [
6
6
  "dsh",
@@ -36,6 +36,7 @@
36
36
  "lib",
37
37
  "cordis.patch.yml",
38
38
  "README.md",
39
+ "CHANGELOG.md",
39
40
  "docs/SECURITY.md"
40
41
  ],
41
42
  "publishConfig": {