@linxin666/dsh-ssh 0.1.19 → 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/README.i18n.yaml +2 -2
- package/README.md +2 -1
- package/README.zh.md +2 -1
- package/lib/client.js +224 -93
- package/lib/client.js.map +1 -1
- package/lib/index.js +182 -91
- package/lib/types/client/locales.d.ts +8 -0
- package/lib/types/client/panel/HostsTab.d.ts +16 -0
- package/lib/types/engine/connection-pool.d.ts +2 -0
- package/lib/types/engine/tunnel.d.ts +4 -2
- package/lib/types/engine.d.ts +7 -0
- package/lib/types/index.d.ts +3 -1
- package/lib/types/mount-once.d.ts +24 -0
- package/lib/types/routes.d.ts +2 -0
- package/lib/types/store.d.ts +6 -0
- package/package.json +12 -12
- package/src/client/locales.ts +16 -0
- package/src/client/panel/HostsTab.tsx +152 -48
- package/src/client/panel/panel.module.css +75 -0
- package/src/engine/connection-pool.ts +23 -3
- package/src/engine/sftp.ts +29 -38
- package/src/engine/tunnel.ts +19 -5
- package/src/engine.ts +11 -0
- package/src/index.ts +4 -1
- package/src/mount-once.ts +48 -0
- package/src/routes.ts +46 -5
- package/src/store.ts +21 -2
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# node scripts/verify-docs.mjs --write <dir>
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 4ea116d416ac311ffde1f1052d9857e46b9c4849
|
|
6
|
+
README.zh.md: d5bec45d8054b999d24832a4a8d103269b2c816d
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Built on the capability list of [badseal/ssh-skill](https://github.com/badseal/s
|
|
|
8
8
|
|
|
9
9
|
| Capability | Description |
|
|
10
10
|
| --- | --- |
|
|
11
|
-
| Host management | CRUD, search, connection test; config stored in `~/.dsh/dsh-ssh.json`; supports key / password auth, passphrase keys, ProxyJump jump hosts (multi-level) |
|
|
11
|
+
| Host management | CRUD, search, connection test; collapsible grouping by environment / tags with per-group batch test; config stored in `~/.dsh/dsh-ssh.json`; supports key / password auth, passphrase keys, ProxyJump jump hosts (multi-level) |
|
|
12
12
|
| Config import | One-click parse of a standard `~/.ssh/config` (Host/HostName/User/Port/IdentityFile/ProxyJump etc.); existing aliases are skipped |
|
|
13
13
|
| Persistent connection pool | Reuses a long-lived connection per host (opposite of the ssh-skill daemon), automatically disconnects after 30 minutes idle, auto-reconnects on disconnect (up to 3 times) |
|
|
14
14
|
| Command execution | exec with a timeout (default 60s), stdout/stderr separated, output truncation guard (2MB) |
|
|
@@ -23,6 +23,7 @@ Built on the capability list of [badseal/ssh-skill](https://github.com/badseal/s
|
|
|
23
23
|
- All `/api/dsh-ssh/*` routes are loopback-only (with same-origin checks) — the interfaces that execute commands against remote servers are not exposed to the LAN.
|
|
24
24
|
- Passwords / key passphrases are stored in plain text in `~/.dsh/dsh-ssh.json`, file mode 0600, directory 0700 (the same trust model as ssh-skill writing passwords into ssh-config comments).
|
|
25
25
|
- Tunnels only listen on `127.0.0.1`.
|
|
26
|
+
- Deleting a host or changing its connection fields (host / port / user / auth / proxyJump) immediately closes that alias's pooled connection and tunnels; later operations reconnect with the new configuration and never reuse a connection authenticated with the old credentials.
|
|
26
27
|
- Before the Agent uses a tool, the host must first be configured in the GUI (or imported from ~/.ssh/config).
|
|
27
28
|
- `ssh_upload` / `ssh_download` read/write arbitrary local paths on this machine with host-process privileges (not through the bash sandbox) — same host-local-path semantics as ssh-skill, be aware of that permission surface.
|
|
28
29
|
- The remote output of exec / cluster is returned verbatim (not sanitized); a command like `env` may bring secrets from the remote environment back into the conversation log.
|
package/README.zh.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
| 能力 | 说明 |
|
|
10
10
|
| --- | --- |
|
|
11
|
-
| 主机管理 |
|
|
11
|
+
| 主机管理 | 增删改查、搜索、连接测试;支持按环境 / 标签分组折叠与组内批量测试;配置存 `~/.dsh/dsh-ssh.json`;支持密钥 / 密码认证、passphrase 密钥、ProxyJump 跳板机(多级) |
|
|
12
12
|
| 配置导入 | 一键解析标准 `~/.ssh/config`(Host/HostName/User/Port/IdentityFile/ProxyJump 等),已有别名自动跳过 |
|
|
13
13
|
| 持久连接池 | 每台主机复用长连接(对应 ssh-skill 的守护进程),空闲 30 分钟自动断开,断线自动重连(最多 3 次) |
|
|
14
14
|
| 命令执行 | exec 带超时(默认 60s),stdout/stderr 分离,输出截断保护(2MB) |
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
- 所有 `/api/dsh-ssh/*` 路由仅限 loopback 访问(含同源校验)——对远程服务器执行命令的接口不会暴露给局域网。
|
|
24
24
|
- 密码 / 密钥口令以明文保存在 `~/.dsh/dsh-ssh.json`,文件权限 0600、目录 0700(与 ssh-skill 把密码写进 ssh-config 注释同一信任模型)。
|
|
25
25
|
- 隧道只监听 `127.0.0.1`。
|
|
26
|
+
- 删除主机或修改其连接字段(host / port / user / auth / proxyJump)会立即断开该别名的池化连接与隧道,后续操作按新配置重新建连,不会复用旧凭据的已认证连接。
|
|
26
27
|
- Agent 使用工具前,主机需先在 GUI 中配置(或从 ~/.ssh/config 导入)。
|
|
27
28
|
- `ssh_upload` / `ssh_download` 以宿主进程权限直接读写本机任意路径(不经 bash 沙箱)——与 ssh-skill 的宿主本地路径语义一致,注意该权限面。
|
|
28
29
|
- exec / cluster 的远程输出原样返回(不脱敏),命令如 `env` 可能把远端环境中的密钥带回对话记录。
|
package/lib/client.js
CHANGED
|
@@ -323,6 +323,14 @@ window.__ModuleLoader__.load({
|
|
|
323
323
|
"hosts.add": "新增主机",
|
|
324
324
|
"hosts.import": "导入 ~/.ssh/config",
|
|
325
325
|
"hosts.search": "搜索别名 / 描述 / 标签…",
|
|
326
|
+
"hosts.groupBy.label": "分组方式",
|
|
327
|
+
"hosts.groupBy.none": "不分组",
|
|
328
|
+
"hosts.groupBy.environment": "按环境分组",
|
|
329
|
+
"hosts.groupBy.tags": "按标签分组",
|
|
330
|
+
"hosts.group.ungrouped": "未设置环境",
|
|
331
|
+
"hosts.group.noTags": "无标签",
|
|
332
|
+
"hosts.group.count": "{count} 台",
|
|
333
|
+
"hosts.group.testAll": "测试全部",
|
|
326
334
|
"hosts.col.alias": "别名",
|
|
327
335
|
"hosts.col.host": "主机",
|
|
328
336
|
"hosts.col.user": "用户",
|
|
@@ -452,6 +460,14 @@ window.__ModuleLoader__.load({
|
|
|
452
460
|
"hosts.add": "Add host",
|
|
453
461
|
"hosts.import": "Import ~/.ssh/config",
|
|
454
462
|
"hosts.search": "Search alias / description / tags…",
|
|
463
|
+
"hosts.groupBy.label": "Group by",
|
|
464
|
+
"hosts.groupBy.none": "No grouping",
|
|
465
|
+
"hosts.groupBy.environment": "Group by environment",
|
|
466
|
+
"hosts.groupBy.tags": "Group by tags",
|
|
467
|
+
"hosts.group.ungrouped": "No environment",
|
|
468
|
+
"hosts.group.noTags": "No tags",
|
|
469
|
+
"hosts.group.count": "{count} hosts",
|
|
470
|
+
"hosts.group.testAll": "Test all",
|
|
455
471
|
"hosts.col.alias": "Alias",
|
|
456
472
|
"hosts.col.host": "Host",
|
|
457
473
|
"hosts.col.user": "User",
|
|
@@ -595,7 +611,7 @@ window.__ModuleLoader__.load({
|
|
|
595
611
|
}
|
|
596
612
|
//#endregion
|
|
597
613
|
//#region \0dsh-css:packages/dsh-ssh/src/client/panel/panel.module.css.mjs
|
|
598
|
-
const css = "[data-pane=conversation],[class*=centerCol]{position:relative}[data-dsh-ssh-view]{z-index:60;background:var(--dsw-alias-bg-base);display:none;position:absolute;inset:0}html[data-dsh-ssh-active]:not([data-dsh-taskboard-active]) [data-dsh-ssh-view]{display:block}html[data-dsh-ssh-active]:not([data-dsh-taskboard-active]) [data-pane=conversation]>:not([data-dsh-ssh-view]),html[data-dsh-ssh-active]:not([data-dsh-taskboard-active]) [class*=centerCol]>:not([data-dsh-ssh-view]){display:none!important}.mL8Uca_entry{width:100%;height:32px;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-radius:8px;align-items:center;gap:8px;padding:0 12px;font-size:13px;display:flex}.mL8Uca_entry:hover{background:var(--dsw-specific-sidebar-nav-item-hover);color:var(--dsw-alias-label-primary)}.mL8Uca_entry[data-active]{background:var(--dsw-specific-sidebar-nav-item-active);color:var(--dsw-alias-label-primary);font-weight:600}.mL8Uca_entryIcon{flex:none;justify-content:center;align-items:center;display:inline-flex}.mL8Uca_entryLabel{text-overflow:ellipsis;overflow:hidden}[data-dsh-frame][data-sidebar-collapsed] .mL8Uca_entry{justify-content:center;width:100%;padding:0}[data-dsh-frame][data-sidebar-collapsed] .mL8Uca_entryLabel{display:none}.mL8Uca_view{overflow:hidden}.mL8Uca_panel{background:var(--dsw-alias-bg-base);min-width:0;height:100%;min-height:0;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);flex-direction:column;gap:10px;padding:14px 16px 16px;display:flex}.mL8Uca_panelHeader{flex:none;align-items:center;gap:10px;display:flex}.mL8Uca_panelTitle{color:var(--dsw-alias-label-primary);white-space:nowrap;flex:1;margin:0;font-size:16px;font-weight:700}.mL8Uca_backButton{align-items:center;gap:4px;display:inline-flex}.mL8Uca_tabBar{border-bottom:1px solid var(--dsw-alias-border-l1);flex:none;gap:2px;display:flex}.mL8Uca_tab{color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-bottom:2px solid #0000;border-radius:6px 6px 0 0;padding:7px 14px;font-size:13px}.mL8Uca_tab:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_tab[data-active]{color:var(--dsw-alias-label-primary);border-bottom-color:var(--dsw-alias-state-business-primary);font-weight:600}.mL8Uca_panelContent{flex-direction:column;flex:1;min-height:0;display:flex;overflow:hidden}.mL8Uca_tabBody{flex-direction:column;flex:1;gap:10px;min-height:0;display:flex;overflow-y:auto}.mL8Uca_fillBody{flex-direction:column;flex:1;gap:10px;min-height:0;display:flex;overflow:hidden}.mL8Uca_toolbar,.mL8Uca_controls{flex-wrap:wrap;flex:none;align-items:center;gap:8px;display:flex}.mL8Uca_controls .mL8Uca_input{flex:0 260px;min-width:150px}.mL8Uca_toolbarSpacer{flex:1}.mL8Uca_search{min-width:120px;color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;flex:0 260px;padding:6px 10px;font-size:13px}.mL8Uca_search::placeholder{color:var(--dsw-alias-label-tertiary)}.mL8Uca_tableWrap{border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex:1;min-height:0;overflow:auto}.mL8Uca_table{border-collapse:collapse;width:100%;font-size:12.5px}.mL8Uca_table th{z-index:1;text-align:left;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);white-space:nowrap;padding:8px 10px;font-weight:600;position:sticky;top:0}.mL8Uca_table td{border-bottom:1px solid var(--dsw-alias-separator-primary);vertical-align:top;padding:7px 10px}.mL8Uca_table tbody tr:last-child td{border-bottom:none}.mL8Uca_table tbody tr:hover td{background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.mL8Uca_cellMuted{color:var(--dsw-alias-label-tertiary)}.mL8Uca_actions{white-space:nowrap;align-items:center;gap:8px;display:flex}.mL8Uca_inlineTest{color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;font-size:11.5px;display:inline-flex}.mL8Uca_inlineTest[data-status=ok]{color:var(--dsw-alias-state-success-primary)}.mL8Uca_inlineTest[data-status=fail]{color:var(--dsw-alias-state-error-primary)}.mL8Uca_badge{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);white-space:nowrap;border-radius:999px;padding:1px 8px;font-size:11px;line-height:1.6;display:inline-block}.mL8Uca_badge[data-kind=key]{color:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary)}.mL8Uca_badge[data-kind=password]{color:var(--dsw-alias-state-warn-primary);border-color:var(--dsw-alias-state-warn-primary)}.mL8Uca_badge[data-status=ok]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.mL8Uca_badge[data-status=fail]{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}.mL8Uca_badge[data-status=timeout]{color:var(--dsw-alias-state-warn-primary);border-color:var(--dsw-alias-state-warn-primary)}.mL8Uca_primaryButton{color:var(--dsw-alias-label-primary-foreground);background:var(--dsw-alias-button-info-fill);cursor:pointer;white-space:nowrap;border:none;border-radius:8px;padding:6px 14px;font-size:13px;font-weight:600}.mL8Uca_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-info-hover)}.mL8Uca_primaryButton:disabled{opacity:.5;cursor:default}.mL8Uca_ghostButton{color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;white-space:nowrap;background:0 0;border-radius:8px;padding:5px 12px;font-size:12px}.mL8Uca_ghostButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_ghostButton:disabled{opacity:.45;cursor:default}.mL8Uca_iconButton{width:26px;height:26px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:0;font-size:13px;display:inline-flex}.mL8Uca_iconButton:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.mL8Uca_linkButton{color:var(--dsw-alias-state-business-primary);cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:0;font-size:12px}.mL8Uca_linkButton:hover:not(:disabled){text-decoration:underline}.mL8Uca_linkButton:disabled{opacity:.45;cursor:default}.mL8Uca_linkButton[data-danger]{color:var(--dsw-alias-state-error-primary)}.mL8Uca_spinner{border:2px solid var(--dsw-alias-state-business-primary);vertical-align:-1px;border-top-color:#0000;border-radius:50%;flex:none;width:11px;height:11px;animation:.8s linear infinite mL8Uca_dshSshSpin;display:inline-block}@keyframes mL8Uca_dshSshSpin{to{transform:rotate(360deg)}}.mL8Uca_banner{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;border-radius:8px;padding:8px 12px;font-size:12.5px;line-height:1.5}.mL8Uca_banner[data-kind=ok]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.mL8Uca_banner[data-kind=error]{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}.mL8Uca_banner[data-kind=info]{color:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary)}.mL8Uca_empty,.mL8Uca_loading{text-align:center;color:var(--dsw-alias-label-tertiary);padding:28px 12px;font-size:12.5px}.mL8Uca_modalBackdrop{z-index:40;background:var(--dsw-alias-bg-mask-1);justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.mL8Uca_modal{background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);width:min(560px,100vw - 48px);max-height:calc(100vh - 96px);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);border-radius:14px;flex-direction:column;gap:12px;padding:18px;display:flex;overflow-y:auto}.mL8Uca_modalTitle{margin:0;font-size:15px;font-weight:700}.mL8Uca_modalFooter{justify-content:flex-end;gap:10px;margin-top:4px;display:flex}.mL8Uca_formError{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px}.mL8Uca_hint{color:var(--dsw-alias-label-tertiary);font-size:11.5px}.mL8Uca_field{flex-direction:column;gap:5px;display:flex}.mL8Uca_fieldLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}.mL8Uca_input{color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);resize:vertical;border-radius:8px;outline:none;padding:7px 10px;font-family:inherit;font-size:13px}.mL8Uca_input:focus{border-color:var(--dsw-alias-state-business-primary)}.mL8Uca_input::placeholder{color:var(--dsw-alias-label-tertiary)}.mL8Uca_input:disabled{opacity:.55}.mL8Uca_formRow{grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px 12px;display:grid}.mL8Uca_radioRow{align-items:center;gap:16px;display:flex}.mL8Uca_radioLabel{color:var(--dsw-alias-label-primary);cursor:pointer;align-items:center;gap:6px;font-size:13px;display:inline-flex}.mL8Uca_termBody{flex-direction:column;flex:1;gap:8px;min-height:0;display:flex;overflow:hidden}.mL8Uca_termWrap{border:1px solid var(--dsw-alias-border-l1);background:#0b0e14;border-radius:10px;flex:1;min-height:0;position:relative;overflow:hidden}.mL8Uca_termContainer{padding:8px 10px;position:absolute;inset:0}.mL8Uca_termPlaceholder{z-index:2;text-align:center;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-base);justify-content:center;align-items:center;padding:0 24px;font-size:12.5px;display:flex;position:absolute;inset:0}.mL8Uca_hiddenFile{display:none}.mL8Uca_browsePanel{border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex-direction:column;flex:none;display:flex;overflow:hidden}.mL8Uca_browseHeader{background:var(--dsw-alias-bg-layer-2);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:center;gap:8px;padding:8px 10px;display:flex}.mL8Uca_browsePath{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);flex:1;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;overflow:hidden}.mL8Uca_browseList{max-height:240px;overflow-y:auto}.mL8Uca_dirRow{text-align:left;width:100%;font:inherit;color:var(--dsw-alias-label-primary);border:none;border-bottom:1px solid var(--dsw-alias-separator-primary);cursor:pointer;background:0 0;grid-template-columns:minmax(0,1fr) auto auto;gap:12px;padding:5px 10px;font-size:12.5px;display:grid}.mL8Uca_dirRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_dirRow[data-type=dir] .mL8Uca_dirName{color:var(--dsw-alias-state-business-primary)}.mL8Uca_dirRow[data-up]{color:var(--dsw-alias-label-secondary)}.mL8Uca_dirName{text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;overflow:hidden}.mL8Uca_dirType{color:var(--dsw-alias-label-tertiary);white-space:nowrap}.mL8Uca_dirSize{color:var(--dsw-alias-label-tertiary);white-space:nowrap;text-align:right}.mL8Uca_transferBlock{flex-direction:column;flex:none;gap:6px;display:flex}.mL8Uca_progressMeta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:12px;font-size:12px;display:flex}.mL8Uca_progressTrack{background:var(--dsw-alias-interactive-bg-hover);border-radius:999px;height:8px;overflow:hidden}.mL8Uca_progressBar{background:var(--dsw-alias-state-business-primary);border-radius:999px;height:100%;transition:width .12s linear}.mL8Uca_tunnelList{flex-direction:column;flex:none;gap:8px;display:flex}.mL8Uca_tunnelRow{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;align-items:center;gap:10px;padding:8px 12px;display:flex}.mL8Uca_tunnelRow[data-state=forwarding]{border-color:var(--dsw-alias-state-success-primary)}.mL8Uca_tunnelRow[data-state=connecting]{border-color:var(--dsw-alias-state-warn-primary)}.mL8Uca_tunnelRow[data-state=failed]{border-color:var(--dsw-alias-state-error-primary)}.mL8Uca_tunnelLabel{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12.5px;overflow:hidden}.mL8Uca_formCard{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex-direction:column;flex:none;gap:10px;padding:12px;display:flex}.mL8Uca_clusterForm{flex-direction:column;flex:none;gap:8px;display:flex}.mL8Uca_clusterFilters{grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;display:grid}.mL8Uca_commandInput{min-height:96px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.mL8Uca_cellDetails summary{cursor:pointer;color:var(--dsw-alias-label-secondary);font-size:12px}.mL8Uca_cellPre{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);white-space:pre-wrap;word-break:break-all;border-radius:6px;max-height:180px;margin:4px 0 0;padding:6px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px;overflow:auto}";
|
|
614
|
+
const css = "[data-pane=conversation],[class*=centerCol]{position:relative}[data-dsh-ssh-view]{z-index:60;background:var(--dsw-alias-bg-base);display:none;position:absolute;inset:0}html[data-dsh-ssh-active]:not([data-dsh-taskboard-active]) [data-dsh-ssh-view]{display:block}html[data-dsh-ssh-active]:not([data-dsh-taskboard-active]) [data-pane=conversation]>:not([data-dsh-ssh-view]),html[data-dsh-ssh-active]:not([data-dsh-taskboard-active]) [class*=centerCol]>:not([data-dsh-ssh-view]){display:none!important}.mL8Uca_entry{width:100%;height:32px;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-radius:8px;align-items:center;gap:8px;padding:0 12px;font-size:13px;display:flex}.mL8Uca_entry:hover{background:var(--dsw-specific-sidebar-nav-item-hover);color:var(--dsw-alias-label-primary)}.mL8Uca_entry[data-active]{background:var(--dsw-specific-sidebar-nav-item-active);color:var(--dsw-alias-label-primary);font-weight:600}.mL8Uca_entryIcon{flex:none;justify-content:center;align-items:center;display:inline-flex}.mL8Uca_entryLabel{text-overflow:ellipsis;overflow:hidden}[data-dsh-frame][data-sidebar-collapsed] .mL8Uca_entry{justify-content:center;width:100%;padding:0}[data-dsh-frame][data-sidebar-collapsed] .mL8Uca_entryLabel{display:none}.mL8Uca_view{overflow:hidden}.mL8Uca_panel{background:var(--dsw-alias-bg-base);min-width:0;height:100%;min-height:0;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);flex-direction:column;gap:10px;padding:14px 16px 16px;display:flex}.mL8Uca_panelHeader{flex:none;align-items:center;gap:10px;display:flex}.mL8Uca_panelTitle{color:var(--dsw-alias-label-primary);white-space:nowrap;flex:1;margin:0;font-size:16px;font-weight:700}.mL8Uca_backButton{align-items:center;gap:4px;display:inline-flex}.mL8Uca_tabBar{border-bottom:1px solid var(--dsw-alias-border-l1);flex:none;gap:2px;display:flex}.mL8Uca_tab{color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-bottom:2px solid #0000;border-radius:6px 6px 0 0;padding:7px 14px;font-size:13px}.mL8Uca_tab:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_tab[data-active]{color:var(--dsw-alias-label-primary);border-bottom-color:var(--dsw-alias-state-business-primary);font-weight:600}.mL8Uca_panelContent{flex-direction:column;flex:1;min-height:0;display:flex;overflow:hidden}.mL8Uca_tabBody{flex-direction:column;flex:1;gap:10px;min-height:0;display:flex;overflow-y:auto}.mL8Uca_fillBody{flex-direction:column;flex:1;gap:10px;min-height:0;display:flex;overflow:hidden}.mL8Uca_toolbar,.mL8Uca_controls{flex-wrap:wrap;flex:none;align-items:center;gap:8px;display:flex}.mL8Uca_controls .mL8Uca_input{flex:0 260px;min-width:150px}.mL8Uca_toolbarSpacer{flex:1}.mL8Uca_search{min-width:120px;color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;flex:0 260px;padding:6px 10px;font-size:13px}.mL8Uca_search::placeholder{color:var(--dsw-alias-label-tertiary)}.mL8Uca_groupBySelect{color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;padding:6px 8px;font-size:13px}.mL8Uca_groupSection+.mL8Uca_groupSection{border-top:1px solid var(--dsw-alias-border-l1)}.mL8Uca_groupHeader{background:var(--dsw-alias-bg-base,var(--dsw-specific-input-major));align-items:center;gap:8px;padding:4px 8px;display:flex;position:sticky;top:0}.mL8Uca_groupToggle{min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;text-align:left;background:0 0;border:none;border-radius:6px;flex:1;align-items:center;gap:6px;padding:4px 6px;font-size:12.5px;font-weight:500;display:flex}.mL8Uca_groupToggle:hover{background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_groupChevron{border-right:1.5px solid var(--dsw-alias-label-tertiary);border-bottom:1.5px solid var(--dsw-alias-label-tertiary);flex:none;width:7px;height:7px;margin-bottom:2px;transition:transform .12s;transform:rotate(45deg)}.mL8Uca_groupChevron[data-collapsed]{margin-bottom:0;transform:rotate(-45deg)}.mL8Uca_groupName{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.mL8Uca_groupCount{color:var(--dsw-alias-label-tertiary);flex:none;font-weight:400}.mL8Uca_tableWrap{border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex:1;min-height:0;overflow:auto}.mL8Uca_table{border-collapse:collapse;width:100%;font-size:12.5px}.mL8Uca_table th{z-index:1;text-align:left;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);white-space:nowrap;padding:8px 10px;font-weight:600;position:sticky;top:0}.mL8Uca_table td{border-bottom:1px solid var(--dsw-alias-separator-primary);vertical-align:top;padding:7px 10px}.mL8Uca_table tbody tr:last-child td{border-bottom:none}.mL8Uca_table tbody tr:hover td{background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.mL8Uca_cellMuted{color:var(--dsw-alias-label-tertiary)}.mL8Uca_actions{white-space:nowrap;align-items:center;gap:8px;display:flex}.mL8Uca_inlineTest{color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;font-size:11.5px;display:inline-flex}.mL8Uca_inlineTest[data-status=ok]{color:var(--dsw-alias-state-success-primary)}.mL8Uca_inlineTest[data-status=fail]{color:var(--dsw-alias-state-error-primary)}.mL8Uca_badge{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);white-space:nowrap;border-radius:999px;padding:1px 8px;font-size:11px;line-height:1.6;display:inline-block}.mL8Uca_badge[data-kind=key]{color:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary)}.mL8Uca_badge[data-kind=password]{color:var(--dsw-alias-state-warn-primary);border-color:var(--dsw-alias-state-warn-primary)}.mL8Uca_badge[data-status=ok]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.mL8Uca_badge[data-status=fail]{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}.mL8Uca_badge[data-status=timeout]{color:var(--dsw-alias-state-warn-primary);border-color:var(--dsw-alias-state-warn-primary)}.mL8Uca_primaryButton{color:var(--dsw-alias-label-primary-foreground);background:var(--dsw-alias-button-info-fill);cursor:pointer;white-space:nowrap;border:none;border-radius:8px;padding:6px 14px;font-size:13px;font-weight:600}.mL8Uca_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-info-hover)}.mL8Uca_primaryButton:disabled{opacity:.5;cursor:default}.mL8Uca_ghostButton{color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;white-space:nowrap;background:0 0;border-radius:8px;padding:5px 12px;font-size:12px}.mL8Uca_ghostButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_ghostButton:disabled{opacity:.45;cursor:default}.mL8Uca_iconButton{width:26px;height:26px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:0;font-size:13px;display:inline-flex}.mL8Uca_iconButton:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.mL8Uca_linkButton{color:var(--dsw-alias-state-business-primary);cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:0;font-size:12px}.mL8Uca_linkButton:hover:not(:disabled){text-decoration:underline}.mL8Uca_linkButton:disabled{opacity:.45;cursor:default}.mL8Uca_linkButton[data-danger]{color:var(--dsw-alias-state-error-primary)}.mL8Uca_spinner{border:2px solid var(--dsw-alias-state-business-primary);vertical-align:-1px;border-top-color:#0000;border-radius:50%;flex:none;width:11px;height:11px;animation:.8s linear infinite mL8Uca_dshSshSpin;display:inline-block}@keyframes mL8Uca_dshSshSpin{to{transform:rotate(360deg)}}.mL8Uca_banner{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;border-radius:8px;padding:8px 12px;font-size:12.5px;line-height:1.5}.mL8Uca_banner[data-kind=ok]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.mL8Uca_banner[data-kind=error]{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}.mL8Uca_banner[data-kind=info]{color:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary)}.mL8Uca_empty,.mL8Uca_loading{text-align:center;color:var(--dsw-alias-label-tertiary);padding:28px 12px;font-size:12.5px}.mL8Uca_modalBackdrop{z-index:40;background:var(--dsw-alias-bg-mask-1);justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.mL8Uca_modal{background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);width:min(560px,100vw - 48px);max-height:calc(100vh - 96px);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);border-radius:14px;flex-direction:column;gap:12px;padding:18px;display:flex;overflow-y:auto}.mL8Uca_modalTitle{margin:0;font-size:15px;font-weight:700}.mL8Uca_modalFooter{justify-content:flex-end;gap:10px;margin-top:4px;display:flex}.mL8Uca_formError{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px}.mL8Uca_hint{color:var(--dsw-alias-label-tertiary);font-size:11.5px}.mL8Uca_field{flex-direction:column;gap:5px;display:flex}.mL8Uca_fieldLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}.mL8Uca_input{color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);resize:vertical;border-radius:8px;outline:none;padding:7px 10px;font-family:inherit;font-size:13px}.mL8Uca_input:focus{border-color:var(--dsw-alias-state-business-primary)}.mL8Uca_input::placeholder{color:var(--dsw-alias-label-tertiary)}.mL8Uca_input:disabled{opacity:.55}.mL8Uca_formRow{grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px 12px;display:grid}.mL8Uca_radioRow{align-items:center;gap:16px;display:flex}.mL8Uca_radioLabel{color:var(--dsw-alias-label-primary);cursor:pointer;align-items:center;gap:6px;font-size:13px;display:inline-flex}.mL8Uca_termBody{flex-direction:column;flex:1;gap:8px;min-height:0;display:flex;overflow:hidden}.mL8Uca_termWrap{border:1px solid var(--dsw-alias-border-l1);background:#0b0e14;border-radius:10px;flex:1;min-height:0;position:relative;overflow:hidden}.mL8Uca_termContainer{padding:8px 10px;position:absolute;inset:0}.mL8Uca_termPlaceholder{z-index:2;text-align:center;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-base);justify-content:center;align-items:center;padding:0 24px;font-size:12.5px;display:flex;position:absolute;inset:0}.mL8Uca_hiddenFile{display:none}.mL8Uca_browsePanel{border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex-direction:column;flex:none;display:flex;overflow:hidden}.mL8Uca_browseHeader{background:var(--dsw-alias-bg-layer-2);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:center;gap:8px;padding:8px 10px;display:flex}.mL8Uca_browsePath{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);flex:1;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;overflow:hidden}.mL8Uca_browseList{max-height:240px;overflow-y:auto}.mL8Uca_dirRow{text-align:left;width:100%;font:inherit;color:var(--dsw-alias-label-primary);border:none;border-bottom:1px solid var(--dsw-alias-separator-primary);cursor:pointer;background:0 0;grid-template-columns:minmax(0,1fr) auto auto;gap:12px;padding:5px 10px;font-size:12.5px;display:grid}.mL8Uca_dirRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_dirRow[data-type=dir] .mL8Uca_dirName{color:var(--dsw-alias-state-business-primary)}.mL8Uca_dirRow[data-up]{color:var(--dsw-alias-label-secondary)}.mL8Uca_dirName{text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;overflow:hidden}.mL8Uca_dirType{color:var(--dsw-alias-label-tertiary);white-space:nowrap}.mL8Uca_dirSize{color:var(--dsw-alias-label-tertiary);white-space:nowrap;text-align:right}.mL8Uca_transferBlock{flex-direction:column;flex:none;gap:6px;display:flex}.mL8Uca_progressMeta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:12px;font-size:12px;display:flex}.mL8Uca_progressTrack{background:var(--dsw-alias-interactive-bg-hover);border-radius:999px;height:8px;overflow:hidden}.mL8Uca_progressBar{background:var(--dsw-alias-state-business-primary);border-radius:999px;height:100%;transition:width .12s linear}.mL8Uca_tunnelList{flex-direction:column;flex:none;gap:8px;display:flex}.mL8Uca_tunnelRow{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;align-items:center;gap:10px;padding:8px 12px;display:flex}.mL8Uca_tunnelRow[data-state=forwarding]{border-color:var(--dsw-alias-state-success-primary)}.mL8Uca_tunnelRow[data-state=connecting]{border-color:var(--dsw-alias-state-warn-primary)}.mL8Uca_tunnelRow[data-state=failed]{border-color:var(--dsw-alias-state-error-primary)}.mL8Uca_tunnelLabel{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12.5px;overflow:hidden}.mL8Uca_formCard{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex-direction:column;flex:none;gap:10px;padding:12px;display:flex}.mL8Uca_clusterForm{flex-direction:column;flex:none;gap:8px;display:flex}.mL8Uca_clusterFilters{grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;display:grid}.mL8Uca_commandInput{min-height:96px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.mL8Uca_cellDetails summary{cursor:pointer;color:var(--dsw-alias-label-secondary);font-size:12px}.mL8Uca_cellPre{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);white-space:pre-wrap;word-break:break-all;border-radius:6px;max-height:180px;margin:4px 0 0;padding:6px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px;overflow:auto}";
|
|
599
615
|
const tagId = "@linxin666/dsh-ssh/panel.module.css";
|
|
600
616
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
601
617
|
const tag = document.createElement("style");
|
|
@@ -636,6 +652,13 @@ window.__ModuleLoader__.load({
|
|
|
636
652
|
"formError": "mL8Uca_formError",
|
|
637
653
|
"formRow": "mL8Uca_formRow",
|
|
638
654
|
"ghostButton": "mL8Uca_ghostButton",
|
|
655
|
+
"groupBySelect": "mL8Uca_groupBySelect",
|
|
656
|
+
"groupChevron": "mL8Uca_groupChevron",
|
|
657
|
+
"groupCount": "mL8Uca_groupCount",
|
|
658
|
+
"groupHeader": "mL8Uca_groupHeader",
|
|
659
|
+
"groupName": "mL8Uca_groupName",
|
|
660
|
+
"groupSection": "mL8Uca_groupSection",
|
|
661
|
+
"groupToggle": "mL8Uca_groupToggle",
|
|
639
662
|
"hiddenFile": "mL8Uca_hiddenFile",
|
|
640
663
|
"hint": "mL8Uca_hint",
|
|
641
664
|
"iconButton": "mL8Uca_iconButton",
|
|
@@ -1238,6 +1261,31 @@ window.__ModuleLoader__.load({
|
|
|
1238
1261
|
* add/edit/delete/test actions, ~/.ssh/config import, and a connect action
|
|
1239
1262
|
* that hands the alias to the terminal tab via onConnect.
|
|
1240
1263
|
*/
|
|
1264
|
+
/**
|
|
1265
|
+
* Bucket hosts into collapsible groups (#379). Grouping by tags places a
|
|
1266
|
+
* multi-tag host in every one of its tag groups (folder view); hosts without
|
|
1267
|
+
* the grouping key land in the '' bucket, which always sorts last. Groups
|
|
1268
|
+
* sort alphabetically; host order inside a group follows the API listing.
|
|
1269
|
+
*/
|
|
1270
|
+
function groupHosts(hosts, groupBy) {
|
|
1271
|
+
if (groupBy === "none") return [{
|
|
1272
|
+
key: "",
|
|
1273
|
+
hosts
|
|
1274
|
+
}];
|
|
1275
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
1276
|
+
const push = (key, host) => {
|
|
1277
|
+
const bucket = buckets.get(key);
|
|
1278
|
+
if (bucket === void 0) buckets.set(key, [host]);
|
|
1279
|
+
else bucket.push(host);
|
|
1280
|
+
};
|
|
1281
|
+
for (const host of hosts) if (groupBy === "environment") push(host.environment ?? "", host);
|
|
1282
|
+
else if (host.tags.length === 0) push("", host);
|
|
1283
|
+
else for (const tag of host.tags) push(tag, host);
|
|
1284
|
+
return [...buckets.entries()].sort(([a], [b]) => a === "" ? 1 : b === "" ? -1 : a.localeCompare(b)).map(([key, group]) => ({
|
|
1285
|
+
key,
|
|
1286
|
+
hosts: group
|
|
1287
|
+
}));
|
|
1288
|
+
}
|
|
1241
1289
|
/** The hosts table plus its toolbar and dialogs. */
|
|
1242
1290
|
function HostsTab({ api, onConnect }) {
|
|
1243
1291
|
const [hosts, setHosts] = (0, react.useState)(null);
|
|
@@ -1248,6 +1296,9 @@ window.__ModuleLoader__.load({
|
|
|
1248
1296
|
const [importing, setImporting] = (0, react.useState)(false);
|
|
1249
1297
|
const [notice, setNotice] = (0, react.useState)(null);
|
|
1250
1298
|
const [dialog, setDialog] = (0, react.useState)(null);
|
|
1299
|
+
const [groupBy, setGroupBy] = (0, react.useState)("none");
|
|
1300
|
+
const [collapsed, setCollapsed] = (0, react.useState)({});
|
|
1301
|
+
const [testingGroup, setTestingGroup] = (0, react.useState)(null);
|
|
1251
1302
|
const seqRef = (0, react.useRef)(0);
|
|
1252
1303
|
const load = (0, react.useCallback)(async (query) => {
|
|
1253
1304
|
const seq = ++seqRef.current;
|
|
@@ -1302,6 +1353,14 @@ window.__ModuleLoader__.load({
|
|
|
1302
1353
|
setError(errorMessage(cause));
|
|
1303
1354
|
}
|
|
1304
1355
|
};
|
|
1356
|
+
const testGroup = async (group) => {
|
|
1357
|
+
setTestingGroup(group.key);
|
|
1358
|
+
try {
|
|
1359
|
+
await Promise.all(group.hosts.map((host) => runTest(host.alias)));
|
|
1360
|
+
} finally {
|
|
1361
|
+
setTestingGroup(null);
|
|
1362
|
+
}
|
|
1363
|
+
};
|
|
1305
1364
|
const importConfig = async () => {
|
|
1306
1365
|
setImporting(true);
|
|
1307
1366
|
try {
|
|
@@ -1318,6 +1377,106 @@ window.__ModuleLoader__.load({
|
|
|
1318
1377
|
setImporting(false);
|
|
1319
1378
|
}
|
|
1320
1379
|
};
|
|
1380
|
+
const renderHostRow = (host) => {
|
|
1381
|
+
const test = testResults[host.alias];
|
|
1382
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
1383
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1384
|
+
className: panel_module_css_default.mono,
|
|
1385
|
+
children: host.alias
|
|
1386
|
+
}),
|
|
1387
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", {
|
|
1388
|
+
className: panel_module_css_default.mono,
|
|
1389
|
+
children: [
|
|
1390
|
+
host.host,
|
|
1391
|
+
":",
|
|
1392
|
+
host.port
|
|
1393
|
+
]
|
|
1394
|
+
}),
|
|
1395
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: host.user }),
|
|
1396
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1397
|
+
className: panel_module_css_default.badge,
|
|
1398
|
+
"data-kind": host.auth,
|
|
1399
|
+
children: host.auth === "key" ? tt("form.auth.key") : tt("form.auth.password")
|
|
1400
|
+
}) }),
|
|
1401
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1402
|
+
className: panel_module_css_default.cellMuted,
|
|
1403
|
+
children: host.environment ?? ""
|
|
1404
|
+
}),
|
|
1405
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1406
|
+
className: panel_module_css_default.cellMuted,
|
|
1407
|
+
children: host.tags.join(", ")
|
|
1408
|
+
}),
|
|
1409
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1410
|
+
className: panel_module_css_default.cellMuted,
|
|
1411
|
+
children: host.description ?? ""
|
|
1412
|
+
}),
|
|
1413
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1414
|
+
className: panel_module_css_default.actions,
|
|
1415
|
+
children: [
|
|
1416
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1417
|
+
type: "button",
|
|
1418
|
+
className: panel_module_css_default.linkButton,
|
|
1419
|
+
disabled: testingAlias === host.alias,
|
|
1420
|
+
onClick: () => {
|
|
1421
|
+
runTest(host.alias);
|
|
1422
|
+
},
|
|
1423
|
+
children: testingAlias === host.alias ? tt("hosts.testing") : tt("hosts.test")
|
|
1424
|
+
}),
|
|
1425
|
+
testingAlias === host.alias && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1426
|
+
className: panel_module_css_default.spinner,
|
|
1427
|
+
"aria-hidden": "true"
|
|
1428
|
+
}),
|
|
1429
|
+
test !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1430
|
+
className: panel_module_css_default.inlineTest,
|
|
1431
|
+
"data-status": test.ok ? "ok" : "fail",
|
|
1432
|
+
children: test.ok ? tt("hosts.testOk", { latency: test.latencyMs ?? 0 }) : tt("hosts.testFail", { error: test.error ?? "" })
|
|
1433
|
+
}),
|
|
1434
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1435
|
+
type: "button",
|
|
1436
|
+
className: panel_module_css_default.linkButton,
|
|
1437
|
+
onClick: () => {
|
|
1438
|
+
setDialog({
|
|
1439
|
+
mode: "edit",
|
|
1440
|
+
host
|
|
1441
|
+
});
|
|
1442
|
+
},
|
|
1443
|
+
children: tt("hosts.edit")
|
|
1444
|
+
}),
|
|
1445
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1446
|
+
type: "button",
|
|
1447
|
+
className: panel_module_css_default.linkButton,
|
|
1448
|
+
"data-danger": true,
|
|
1449
|
+
onClick: () => {
|
|
1450
|
+
deleteHost(host.alias);
|
|
1451
|
+
},
|
|
1452
|
+
children: tt("hosts.delete")
|
|
1453
|
+
}),
|
|
1454
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1455
|
+
type: "button",
|
|
1456
|
+
className: panel_module_css_default.ghostButton,
|
|
1457
|
+
onClick: () => {
|
|
1458
|
+
onConnect(host.alias);
|
|
1459
|
+
},
|
|
1460
|
+
children: tt("hosts.connected")
|
|
1461
|
+
})
|
|
1462
|
+
]
|
|
1463
|
+
}) })
|
|
1464
|
+
] }, host.alias);
|
|
1465
|
+
};
|
|
1466
|
+
const renderHostTable = (rows) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
|
|
1467
|
+
className: panel_module_css_default.table,
|
|
1468
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
1469
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: tt("hosts.col.alias") }),
|
|
1470
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: tt("hosts.col.host") }),
|
|
1471
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: tt("hosts.col.user") }),
|
|
1472
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: tt("hosts.col.auth") }),
|
|
1473
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: tt("hosts.col.environment") }),
|
|
1474
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: tt("hosts.col.tags") }),
|
|
1475
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: tt("hosts.col.description") }),
|
|
1476
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: tt("hosts.col.actions") })
|
|
1477
|
+
] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: rows.map(renderHostRow) })]
|
|
1478
|
+
});
|
|
1479
|
+
const groups = hosts === null ? [] : groupHosts(hosts, groupBy);
|
|
1321
1480
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1322
1481
|
className: panel_module_css_default.fillBody,
|
|
1323
1482
|
children: [
|
|
@@ -1333,6 +1492,28 @@ window.__ModuleLoader__.load({
|
|
|
1333
1492
|
setSearch(event.target.value);
|
|
1334
1493
|
}
|
|
1335
1494
|
}),
|
|
1495
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1496
|
+
className: panel_module_css_default.groupBySelect,
|
|
1497
|
+
"aria-label": tt("hosts.groupBy.label"),
|
|
1498
|
+
value: groupBy,
|
|
1499
|
+
onChange: (event) => {
|
|
1500
|
+
setGroupBy(event.target.value);
|
|
1501
|
+
},
|
|
1502
|
+
children: [
|
|
1503
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1504
|
+
value: "none",
|
|
1505
|
+
children: tt("hosts.groupBy.none")
|
|
1506
|
+
}),
|
|
1507
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1508
|
+
value: "environment",
|
|
1509
|
+
children: tt("hosts.groupBy.environment")
|
|
1510
|
+
}),
|
|
1511
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1512
|
+
value: "tags",
|
|
1513
|
+
children: tt("hosts.groupBy.tags")
|
|
1514
|
+
})
|
|
1515
|
+
]
|
|
1516
|
+
}),
|
|
1336
1517
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: panel_module_css_default.toolbarSpacer }),
|
|
1337
1518
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1338
1519
|
type: "button",
|
|
@@ -1371,105 +1552,55 @@ window.__ModuleLoader__.load({
|
|
|
1371
1552
|
className: panel_module_css_default.empty,
|
|
1372
1553
|
children: tt("hosts.empty")
|
|
1373
1554
|
}),
|
|
1374
|
-
hosts !== null && hosts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1555
|
+
hosts !== null && hosts.length > 0 && groupBy === "none" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1375
1556
|
className: panel_module_css_default.tableWrap,
|
|
1376
|
-
children:
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
host.host,
|
|
1398
|
-
":",
|
|
1399
|
-
host.port
|
|
1400
|
-
]
|
|
1401
|
-
}),
|
|
1402
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: host.user }),
|
|
1403
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1404
|
-
className: panel_module_css_default.badge,
|
|
1405
|
-
"data-kind": host.auth,
|
|
1406
|
-
children: host.auth === "key" ? tt("form.auth.key") : tt("form.auth.password")
|
|
1407
|
-
}) }),
|
|
1408
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1409
|
-
className: panel_module_css_default.cellMuted,
|
|
1410
|
-
children: host.environment ?? ""
|
|
1411
|
-
}),
|
|
1412
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1413
|
-
className: panel_module_css_default.cellMuted,
|
|
1414
|
-
children: host.tags.join(", ")
|
|
1415
|
-
}),
|
|
1416
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1417
|
-
className: panel_module_css_default.cellMuted,
|
|
1418
|
-
children: host.description ?? ""
|
|
1419
|
-
}),
|
|
1420
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1421
|
-
className: panel_module_css_default.actions,
|
|
1557
|
+
children: renderHostTable(hosts)
|
|
1558
|
+
}),
|
|
1559
|
+
hosts !== null && hosts.length > 0 && groupBy !== "none" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1560
|
+
className: panel_module_css_default.tableWrap,
|
|
1561
|
+
children: groups.map((group) => {
|
|
1562
|
+
const isCollapsed = collapsed[group.key] === true;
|
|
1563
|
+
const label = group.key === "" ? groupBy === "tags" ? tt("hosts.group.noTags") : tt("hosts.group.ungrouped") : group.key;
|
|
1564
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1565
|
+
className: panel_module_css_default.groupSection,
|
|
1566
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1567
|
+
className: panel_module_css_default.groupHeader,
|
|
1568
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1569
|
+
type: "button",
|
|
1570
|
+
className: panel_module_css_default.groupToggle,
|
|
1571
|
+
"aria-expanded": !isCollapsed,
|
|
1572
|
+
onClick: () => {
|
|
1573
|
+
setCollapsed((prev) => ({
|
|
1574
|
+
...prev,
|
|
1575
|
+
[group.key]: !isCollapsed
|
|
1576
|
+
}));
|
|
1577
|
+
},
|
|
1422
1578
|
children: [
|
|
1423
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
disabled: testingAlias === host.alias,
|
|
1427
|
-
onClick: () => {
|
|
1428
|
-
runTest(host.alias);
|
|
1429
|
-
},
|
|
1430
|
-
children: testingAlias === host.alias ? tt("hosts.testing") : tt("hosts.test")
|
|
1431
|
-
}),
|
|
1432
|
-
testingAlias === host.alias && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1433
|
-
className: panel_module_css_default.spinner,
|
|
1579
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1580
|
+
className: panel_module_css_default.groupChevron,
|
|
1581
|
+
"data-collapsed": isCollapsed || void 0,
|
|
1434
1582
|
"aria-hidden": "true"
|
|
1435
1583
|
}),
|
|
1436
|
-
|
|
1437
|
-
className: panel_module_css_default.
|
|
1438
|
-
|
|
1439
|
-
children: test.ok ? tt("hosts.testOk", { latency: test.latencyMs ?? 0 }) : tt("hosts.testFail", { error: test.error ?? "" })
|
|
1584
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1585
|
+
className: panel_module_css_default.groupName,
|
|
1586
|
+
children: label
|
|
1440
1587
|
}),
|
|
1441
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
onClick: () => {
|
|
1445
|
-
setDialog({
|
|
1446
|
-
mode: "edit",
|
|
1447
|
-
host
|
|
1448
|
-
});
|
|
1449
|
-
},
|
|
1450
|
-
children: tt("hosts.edit")
|
|
1451
|
-
}),
|
|
1452
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1453
|
-
type: "button",
|
|
1454
|
-
className: panel_module_css_default.linkButton,
|
|
1455
|
-
"data-danger": true,
|
|
1456
|
-
onClick: () => {
|
|
1457
|
-
deleteHost(host.alias);
|
|
1458
|
-
},
|
|
1459
|
-
children: tt("hosts.delete")
|
|
1460
|
-
}),
|
|
1461
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1462
|
-
type: "button",
|
|
1463
|
-
className: panel_module_css_default.ghostButton,
|
|
1464
|
-
onClick: () => {
|
|
1465
|
-
onConnect(host.alias);
|
|
1466
|
-
},
|
|
1467
|
-
children: tt("hosts.connected")
|
|
1588
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1589
|
+
className: panel_module_css_default.groupCount,
|
|
1590
|
+
children: tt("hosts.group.count", { count: group.hosts.length })
|
|
1468
1591
|
})
|
|
1469
1592
|
]
|
|
1470
|
-
})
|
|
1471
|
-
|
|
1472
|
-
|
|
1593
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1594
|
+
type: "button",
|
|
1595
|
+
className: panel_module_css_default.linkButton,
|
|
1596
|
+
disabled: testingGroup === group.key,
|
|
1597
|
+
onClick: () => {
|
|
1598
|
+
testGroup(group);
|
|
1599
|
+
},
|
|
1600
|
+
children: testingGroup === group.key ? tt("hosts.testing") : tt("hosts.group.testAll")
|
|
1601
|
+
})]
|
|
1602
|
+
}), !isCollapsed && renderHostTable(group.hosts)]
|
|
1603
|
+
}, group.key);
|
|
1473
1604
|
})
|
|
1474
1605
|
}),
|
|
1475
1606
|
dialog !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HostFormDialog, {
|