dsh-ssh-manage 0.3.6 → 0.3.7
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/README.en.md +4 -4
- package/README.md +4 -4
- package/client.js +53 -4
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
`dsh-ssh-manage` is a Web plugin for [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness). It turns the hosts in `~/.ssh/config` into manageable objects in the DSH sidebar:
|
|
24
24
|
|
|
25
|
-
- **Host management** — scan the config, groups, tags, favorites, notes, latency and host inspection;
|
|
25
|
+
- **Host management** — scan the config, groups, tags, favorites, notes, latency and host inspection; SSH aliases are globally unique, so use the repeatable Display name for same names across groups;
|
|
26
26
|
- **Interactive terminal** — ssh2 allocates a PTY, xterm.js renders it, with colors, arrow keys, full-screen programs such as `vim`/`top`, and window resizing;
|
|
27
27
|
- **Backup and migration** — export/import hosts, groups, tags, notes, favorites and passwords from Settings; exports are re-encrypted with a passphrase (scrypt + AES-256-GCM) and imports merge by alias;
|
|
28
28
|
- **Day-to-day operations** — batch commands, SFTP browse/mkdir/rename/delete/upload/download, connection tests;
|
|
@@ -97,16 +97,16 @@ Creating a host requires alias, hostname and password. The password is not writt
|
|
|
97
97
|
|
|
98
98
|
### From npm (recommended)
|
|
99
99
|
|
|
100
|
-
npm package: [`dsh-ssh-manage`](https://www.npmjs.com/package/dsh-ssh-manage) (current version `0.3.
|
|
100
|
+
npm package: [`dsh-ssh-manage`](https://www.npmjs.com/package/dsh-ssh-manage) (current version `0.3.7`).
|
|
101
101
|
|
|
102
102
|
```text
|
|
103
|
-
plugin_manager action=install_bundle target=dsh-ssh-manage@0.3.
|
|
103
|
+
plugin_manager action=install_bundle target=dsh-ssh-manage@0.3.7
|
|
104
104
|
```
|
|
105
105
|
|
|
106
106
|
Or with the DSH CLI:
|
|
107
107
|
|
|
108
108
|
```bash
|
|
109
|
-
dsh plugin add dsh-ssh-manage@0.3.
|
|
109
|
+
dsh plugin add dsh-ssh-manage@0.3.7
|
|
110
110
|
```
|
|
111
111
|
|
|
112
112
|
After installation, refresh the Web page; the sidebar shows an "SSH Hosts" entry with id `dsh-ssh-manage`.
|
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
`dsh-ssh-manage` 是 [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) 的 Web 插件。它把 `~/.ssh/config` 里的主机变成 DSH 侧边栏里可管理、可连接、可运维的对象:
|
|
24
24
|
|
|
25
|
-
- **主机管理**:扫描配置、分组、标签、收藏、备注、连接延迟、主机巡检;
|
|
25
|
+
- **主机管理**:扫描配置、分组、标签、收藏、备注、连接延迟、主机巡检;SSH 别名全局唯一,不同分组如需同名请使用可重复的“显示名称”;
|
|
26
26
|
- **交互式终端**:ssh2 分配 PTY,xterm.js 渲染,支持彩色输出、方向键、`vim`/`top` 等全屏程序和窗口自适应;
|
|
27
27
|
- **备份迁移**:设置页可导出/导入主机、分组、标签、备注、收藏和密码;导出文件用自定义口令二次加密(scrypt + AES-256-GCM),导入按别名合并;
|
|
28
28
|
- **日常运维**:批量命令、SFTP 目录浏览 / 新建 / 重命名 / 删除 / 上传 / 下载、测试连接;
|
|
@@ -99,16 +99,16 @@
|
|
|
99
99
|
|
|
100
100
|
### 从 npm 安装(推荐)
|
|
101
101
|
|
|
102
|
-
npm 包:[`dsh-ssh-manage`](https://www.npmjs.com/package/dsh-ssh-manage)(当前版本 `0.3.
|
|
102
|
+
npm 包:[`dsh-ssh-manage`](https://www.npmjs.com/package/dsh-ssh-manage)(当前版本 `0.3.7`)。
|
|
103
103
|
|
|
104
104
|
```text
|
|
105
|
-
plugin_manager action=install_bundle target=dsh-ssh-manage@0.3.
|
|
105
|
+
plugin_manager action=install_bundle target=dsh-ssh-manage@0.3.7
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
或使用 DSH CLI:
|
|
109
109
|
|
|
110
110
|
```bash
|
|
111
|
-
dsh plugin add dsh-ssh-manage@0.3.
|
|
111
|
+
dsh plugin add dsh-ssh-manage@0.3.7
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
安装后刷新 Web 页面,左侧栏会出现「SSH 主机」入口,入口 id 为 `dsh-ssh-manage`。
|
package/client.js
CHANGED
|
@@ -126,7 +126,7 @@ window.__ModuleLoader__.load({
|
|
|
126
126
|
'editor.editTitle': ['编辑 SSH 主机', 'Edit SSH host'],
|
|
127
127
|
'editor.alias': ['别名', 'Alias'],
|
|
128
128
|
'editor.aliasPlaceholder': ['prod', 'prod'],
|
|
129
|
-
'editor.aliasHint': ['~/.ssh/config 中的 Host
|
|
129
|
+
'editor.aliasHint': ['~/.ssh/config 中的 Host 别名,必须全局唯一;不同分组要显示同名,请使用“显示名称”。', 'The Host alias in ~/.ssh/config must be globally unique. To show the same name in different groups, use Display name.'],
|
|
130
130
|
'editor.hostname': ['服务器地址', 'Hostname'],
|
|
131
131
|
'editor.hostnamePlaceholder': ['203.0.113.10 或 example.com', '203.0.113.10 or example.com'],
|
|
132
132
|
'editor.user': ['用户', 'User'],
|
|
@@ -141,6 +141,9 @@ window.__ModuleLoader__.load({
|
|
|
141
141
|
'editor.tagsPlaceholder': ['web, critical(逗号分隔)', 'web, critical (comma separated)'],
|
|
142
142
|
'editor.label': ['显示名称', 'Display name'],
|
|
143
143
|
'editor.labelPlaceholder': ['例如 生产 API', 'e.g. Production API'],
|
|
144
|
+
'editor.labelHint': ['显示名称可以重复,列表和终端标签优先显示它;SSH 别名仍需唯一。', 'Display names may repeat; lists and terminal tabs prefer it. The SSH alias must still be unique.'],
|
|
145
|
+
'editor.aliasExists': ['SSH 别名“{alias}”已被主机“{host}”使用(分组:{group})。SSH 要求别名全局唯一;如果只是想在另一个分组显示同名,请改用建议别名,并把“显示名称”设为 {alias}。', 'SSH alias "{alias}" is already used by "{host}" (group: {group}). SSH aliases must be globally unique. To show the same name in another group, use the suggested alias and set Display name to {alias}.'],
|
|
146
|
+
'editor.useSuggestedAlias': ['使用建议别名 {alias}', 'Use suggested alias {alias}'],
|
|
144
147
|
'editor.note': ['备注', 'Notes'],
|
|
145
148
|
'editor.notePlaceholder': ['给这台主机的说明,会显示在列表里', 'A short note shown in the list'],
|
|
146
149
|
'editor.passwordSection': ['服务器密码', 'Server password'],
|
|
@@ -4729,6 +4732,7 @@ window.__ModuleLoader__.load({
|
|
|
4729
4732
|
const [password, setPassword] = React.useState('')
|
|
4730
4733
|
const [passwordError, setPasswordError] = React.useState('')
|
|
4731
4734
|
const [formError, setFormError] = React.useState('')
|
|
4735
|
+
const [aliasSuggestion, setAliasSuggestion] = React.useState('')
|
|
4732
4736
|
const [groupMenuOpen, setGroupMenuOpen] = React.useState(false)
|
|
4733
4737
|
const groupMenuRef = React.useRef(null)
|
|
4734
4738
|
const groupOptions = (model.allGroups ?? []).filter((group) => group.key !== '').map((group) => group.key)
|
|
@@ -4798,6 +4802,28 @@ window.__ModuleLoader__.load({
|
|
|
4798
4802
|
setFormError('')
|
|
4799
4803
|
return
|
|
4800
4804
|
}
|
|
4805
|
+
const collision = hosts.find((host) =>
|
|
4806
|
+
host.host !== state.original
|
|
4807
|
+
&& (host.host === alias || (Array.isArray(host.aliases) && host.aliases.includes(alias))),
|
|
4808
|
+
)
|
|
4809
|
+
if (collision) {
|
|
4810
|
+
const groupPart = draft.group.trim().replace(/[^A-Za-z0-9_.@-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 20)
|
|
4811
|
+
let suggested = groupPart ? `${alias}-${groupPart}` : `${alias}-2`
|
|
4812
|
+
let counter = 2
|
|
4813
|
+
while (hosts.some((host) => host.host === suggested || (Array.isArray(host.aliases) && host.aliases.includes(suggested)))) {
|
|
4814
|
+
suggested = `${alias}-${counter}`
|
|
4815
|
+
counter += 1
|
|
4816
|
+
}
|
|
4817
|
+
suggested = suggested.slice(0, 64)
|
|
4818
|
+
setAliasSuggestion(suggested)
|
|
4819
|
+
setFormError(t('editor.aliasExists', {
|
|
4820
|
+
alias,
|
|
4821
|
+
host: hostName(collision),
|
|
4822
|
+
group: collision.group || t('tree.ungrouped'),
|
|
4823
|
+
}))
|
|
4824
|
+
return
|
|
4825
|
+
}
|
|
4826
|
+
setAliasSuggestion('')
|
|
4801
4827
|
setPasswordError('')
|
|
4802
4828
|
setFormError('')
|
|
4803
4829
|
const saved = await saveEditor({
|
|
@@ -4860,13 +4886,36 @@ window.__ModuleLoader__.load({
|
|
|
4860
4886
|
h(
|
|
4861
4887
|
'div',
|
|
4862
4888
|
{ className: 'ssh-formSection' },
|
|
4863
|
-
formError
|
|
4889
|
+
formError
|
|
4890
|
+
? h(
|
|
4891
|
+
'div',
|
|
4892
|
+
{ className: 'ssh-formError' },
|
|
4893
|
+
h(IconAlert, { size: 13 }),
|
|
4894
|
+
h('span', null, formError),
|
|
4895
|
+
aliasSuggestion
|
|
4896
|
+
? h(
|
|
4897
|
+
Button,
|
|
4898
|
+
{
|
|
4899
|
+
size: 'sm',
|
|
4900
|
+
variant: 'ghost',
|
|
4901
|
+
onClick: () => {
|
|
4902
|
+
if (!draft.label.trim()) update('label', alias)
|
|
4903
|
+
update('host', aliasSuggestion)
|
|
4904
|
+
setAliasSuggestion('')
|
|
4905
|
+
setFormError('')
|
|
4906
|
+
},
|
|
4907
|
+
},
|
|
4908
|
+
t('editor.useSuggestedAlias', { alias: aliasSuggestion }),
|
|
4909
|
+
)
|
|
4910
|
+
: null,
|
|
4911
|
+
)
|
|
4912
|
+
: null,
|
|
4864
4913
|
h(
|
|
4865
4914
|
'div',
|
|
4866
4915
|
{ className: 'ssh-formGrid' },
|
|
4867
4916
|
h(
|
|
4868
4917
|
Field,
|
|
4869
|
-
{ label: t('editor.alias') },
|
|
4918
|
+
{ label: t('editor.alias'), hint: t('editor.aliasHint') },
|
|
4870
4919
|
h(TextInput, {
|
|
4871
4920
|
value: draft.host,
|
|
4872
4921
|
onChange: (value) => update('host', value),
|
|
@@ -4964,7 +5013,7 @@ window.__ModuleLoader__.load({
|
|
|
4964
5013
|
),
|
|
4965
5014
|
h(
|
|
4966
5015
|
Field,
|
|
4967
|
-
{ label: t('editor.label') },
|
|
5016
|
+
{ label: t('editor.label'), hint: t('editor.labelHint') },
|
|
4968
5017
|
h(TextInput, {
|
|
4969
5018
|
value: draft.label,
|
|
4970
5019
|
onChange: (value) => update('label', value),
|