@linxin666/dsh-ssh 0.4.4 → 0.4.6
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 +16 -2
- package/README.zh.md +16 -2
- package/lib/client.js +62 -14
- package/lib/client.js.map +1 -1
- package/lib/index.js +193 -41
- package/lib/types/client/api.d.ts +21 -10
- package/lib/types/client/locales.d.ts +1 -1
- package/lib/types/engine/cluster.d.ts +2 -0
- package/lib/types/engine/connection-pool.d.ts +2 -2
- package/lib/types/engine/tunnel.d.ts +2 -0
- package/lib/types/engine.d.ts +2 -1
- package/lib/types/tool-surface.d.ts +71 -0
- package/package.json +18 -22
- package/src/client/api.ts +72 -15
- package/src/client/locales.ts +2 -2
- package/src/engine/cluster.ts +3 -1
- package/src/engine/connection-pool.ts +47 -2
- package/src/engine/tunnel.ts +74 -19
- package/src/engine.ts +3 -2
- package/src/index.ts +11 -3
- package/src/tool-surface.ts +95 -0
- package/src/tools.ts +15 -16
- package/lib/invariant.js +0 -6
- package/lib/types/invariant.d.ts +0 -3
- package/src/invariant.ts +0 -4
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: 9031d9df52f51011cc951cba61665da91b42c65b
|
|
6
|
+
README.zh.md: ddae66eb2e06138a8c9157dcfd07da00acdba0f1
|
package/README.md
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
|
-
# dsh-ssh
|
|
1
|
+
# dsh-ssh · Web SSH Terminal, SFTP Transfer & Remote Operations for DeepSeek Harness (DSH)
|
|
2
2
|
|
|
3
3
|
English | [中文](README.zh.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img src="https://img.shields.io/npm/v/@linxin666/dsh-ssh?style=flat-square" alt="Version">
|
|
7
|
+
|
|
8
|
+
<img src="https://img.shields.io/badge/DSH-%3E%3D0.2.0--rc.2-4c6ef5?style=flat-square&labelColor=454a54" alt="DSH">
|
|
9
|
+
|
|
10
|
+
<img src="https://img.shields.io/badge/license-Apache--2.0-blue?style=flat-square" alt="License">
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<strong>Remote Linux Server Operations & Web SSH Suite for DeepSeek Harness (DSH)</strong><br>
|
|
15
|
+
<em>xterm.js Web Terminal · Visual SFTP Transfer · Port Forwarding Tunnels · Cluster Execution · 6 Agent Tools</em>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
A dedicated remote Linux server operations and cluster management plugin for DeepSeek Harness (DSH) Web GUI and official desktop client: in-process persistent connection pool, Web GUI host inventory management, xterm.js terminal, visual SFTP file transfer, port forwarding tunnels, and 6 dedicated agent tools implemented on top of official NPM SDKs without modifying DSH core source code.
|
|
6
19
|
|
|
7
20
|
## Capabilities
|
|
8
21
|
|
|
@@ -27,6 +40,7 @@ The panel loads its contents on first open. Closing and reopening it preserves t
|
|
|
27
40
|
- ssh-agent auth stores only the agent socket path (or the special value `pageant`); it never reads or stores private-key material.
|
|
28
41
|
- Tunnels only listen on `127.0.0.1`.
|
|
29
42
|
- Deleting a host or changing its connection fields (host / port / user / auth / proxyJump / proxyCommand) 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.
|
|
43
|
+
- When the SSH connection behind a tunnel drops, the tunnel retires itself: the local port stops accepting, the tunnel leaves the list, and the connection that arrives during the drop is closed without traffic instead of interrupting the host process.
|
|
30
44
|
- A `proxyCommand` value is a shell command executed by the DSH host process with its privileges — exactly the trust `ssh(1)` gives the same line in `~/.ssh/config`. It can only come from the user's own 0600 store file: the Agent cannot create or edit hosts, and `ssh_list` reports only whether a host has one, never the command text.
|
|
31
45
|
- Before the Agent uses a tool, the host must first be configured in the GUI (or imported from ~/.ssh/config).
|
|
32
46
|
- `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.
|
package/README.zh.md
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
|
-
# dsh-ssh
|
|
1
|
+
# dsh-ssh · DeepSeek Harness (DSH) 网页版 SSH 终端、SFTP 文件传输与集群运维插件
|
|
2
2
|
|
|
3
3
|
[English](README.md) | 中文
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img src="https://img.shields.io/npm/v/@linxin666/dsh-ssh?style=flat-square" alt="Version">
|
|
7
|
+
|
|
8
|
+
<img src="https://img.shields.io/badge/DSH-%3E%3D0.2.0--rc.2-4c6ef5?style=flat-square&labelColor=454a54" alt="DSH">
|
|
9
|
+
|
|
10
|
+
<img src="https://img.shields.io/badge/license-Apache--2.0-blue?style=flat-square" alt="License">
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<strong>DeepSeek Harness(DSH)官方 Web GUI 与桌面客户端专属远程 Linux 服务器运维套件</strong><br>
|
|
15
|
+
<em>xterm.js Web 终端 · 可视化 SFTP 传输 · 本地端口转发隧道 · 集群并发命令 · 6 大 Agent 工具</em>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
专为 DeepSeek Harness(DSH)Web GUI 与官方桌面客户端定制的远程 Linux 服务器运维与集群管理插件:Host 进程内的持久连接池 + Web GUI 主机管理面板 + 基于 xterm.js 的 Web 终端 + 可视化 SFTP 传输 + 6 大专用 Agent 工具,全部通过官方 NPM SDK 实现,零修改侵入 DSH 官方源码。
|
|
6
19
|
|
|
7
20
|
## 能力
|
|
8
21
|
|
|
@@ -27,6 +40,7 @@
|
|
|
27
40
|
- ssh-agent 认证仅保存 agent socket 路径(或 `pageant` 特殊值),不读取也不保存任何私钥材料。
|
|
28
41
|
- 隧道只监听 `127.0.0.1`。
|
|
29
42
|
- 删除主机或修改其连接字段(host / port / user / auth / proxyJump / proxyCommand)会立即断开该别名的池化连接与隧道,后续操作按新配置重新建连,不会复用旧凭据的已认证连接。
|
|
43
|
+
- 隧道背后的 SSH 连接一旦断开,隧道会自行退役:本地端口不再接受连接,隧道从列表中消失;断开期间到达的连接被直接关闭且没有流量,不会打断宿主进程。
|
|
30
44
|
- `proxyCommand` 是一条由 DSH 宿主进程以其自身权限执行的 shell 命令——与 `ssh(1)` 执行 `~/.ssh/config` 里同一行的信任模型一致。它只能来自用户自己的 0600 配置文件:Agent 无法创建或修改主机,`ssh_list` 只报告该主机是否配置了 ProxyCommand,不返回命令原文。
|
|
31
45
|
- Agent 使用工具前,主机需先在 GUI 中配置(或从 ~/.ssh/config 导入)。
|
|
32
46
|
- `ssh_upload` / `ssh_download` 以宿主进程权限直接读写本机任意路径(不经 bash 沙箱)——与 ssh-skill 的宿主本地路径语义一致,注意该权限面。
|
package/lib/client.js
CHANGED
|
@@ -115,7 +115,7 @@ window.__ModuleLoader__.load({
|
|
|
115
115
|
"terminal.ready": "终端已连接({alias})",
|
|
116
116
|
"terminal.exited": "终端已退出({alias})",
|
|
117
117
|
"terminal.error": "终端错误:{error}",
|
|
118
|
-
"terminal.noWebSocket": "当前页面不支持 WebSocket
|
|
118
|
+
"terminal.noWebSocket": "当前页面不支持 WebSocket(应用外壳没有可连接的本地主机地址),请改用浏览器打开的 Web 界面使用终端。",
|
|
119
119
|
"terminal.auth.title": "二次身份验证 (2FA / 交互式认证)",
|
|
120
120
|
"terminal.auth.submit": "提交验证码",
|
|
121
121
|
"terminal.auth.cancel": "取消",
|
|
@@ -271,7 +271,7 @@ window.__ModuleLoader__.load({
|
|
|
271
271
|
"terminal.ready": "Terminal connected ({alias})",
|
|
272
272
|
"terminal.exited": "Terminal exited ({alias})",
|
|
273
273
|
"terminal.error": "Terminal error: {error}",
|
|
274
|
-
"terminal.noWebSocket": "This page cannot carry a WebSocket (the application shell
|
|
274
|
+
"terminal.noWebSocket": "This page cannot carry a WebSocket (the application shell published no local host address to reach). Open the Web UI in a browser to use the terminal.",
|
|
275
275
|
"terminal.auth.title": "Two-Factor Authentication (2FA)",
|
|
276
276
|
"terminal.auth.submit": "Submit Code",
|
|
277
277
|
"terminal.auth.cancel": "Cancel",
|
|
@@ -453,23 +453,71 @@ window.__ModuleLoader__.load({
|
|
|
453
453
|
* The WebSocket URL the terminal route is reached at, or undefined when this
|
|
454
454
|
* page cannot carry one.
|
|
455
455
|
*
|
|
456
|
-
* A page
|
|
457
|
-
* Desktop shell serves its Web GUI from `dsh-app://app/`) has no WebSocket
|
|
458
|
-
* transport: its scheme handler forwards HTTP requests to the local host but
|
|
459
|
-
* cannot upgrade a socket, so `ws://app/...` never connects and the terminal
|
|
460
|
-
* reports "connection error" (issue #1744). Every other page is a web page and
|
|
461
|
-
* resolves the socket against its own origin, exactly as before.
|
|
456
|
+
* A web page resolves the socket against its own origin, exactly as before.
|
|
462
457
|
*
|
|
463
|
-
* A
|
|
464
|
-
*
|
|
465
|
-
*
|
|
458
|
+
* A page delivered by an application on this machine (the official DSH Desktop
|
|
459
|
+
* shell serves its Web GUI from `dsh-app://app/`) cannot carry a socket on its
|
|
460
|
+
* own scheme: the protocol handler forwards HTTP to the local Host but has no
|
|
461
|
+
* upgrade to forward. That is exactly the case the official transport hook
|
|
462
|
+
* covers - the shell publishes `__DSH_TRANSPORT__.streamBaseUrl`, the loopback
|
|
463
|
+
* authority of the Host it owns and already forwards every other request to
|
|
464
|
+
* (issue #1744). The socket is therefore dialed there instead, with the `ws`/
|
|
465
|
+
* `wss` scheme derived from that base, which is what the shell's
|
|
466
|
+
* `onBeforeSendHeaders` hook expects to see before it attaches its
|
|
467
|
+
* authority-bound credential.
|
|
468
|
+
*
|
|
469
|
+
* The base is used only on an application-delivered page. A web page that
|
|
470
|
+
* somehow carries the hook (the remote channel grants `ownsHost` to a paired
|
|
471
|
+
* LAN page, and that page is fenced behind a pairing channel) keeps resolving
|
|
472
|
+
* against its own origin, so this cannot reroute gated traffic onto a host the
|
|
473
|
+
* page is not entitled to reach. A blank authority is treated the same as an
|
|
474
|
+
* application scheme with no base: there is nothing to dial, and the caller
|
|
475
|
+
* gets the actionable reason instead of a socket that can only fail.
|
|
466
476
|
*
|
|
467
477
|
* @param location - the page location to read.
|
|
468
478
|
* @param search - the query string carrying `alias` or `session`.
|
|
479
|
+
* @param streamBaseUrl - `__DSH_TRANSPORT__.streamBaseUrl`, when the shell published one.
|
|
469
480
|
* @returns the absolute `ws:`/`wss:` URL, or undefined when the page cannot carry one.
|
|
470
481
|
*/
|
|
471
|
-
function terminalSocketUrl(location, search) {
|
|
482
|
+
function terminalSocketUrl(location, search, streamBaseUrl) {
|
|
472
483
|
if (WEB_PAGE_PROTOCOLS.includes(location.protocol)) return (location.protocol === "https:" ? "wss" : "ws") + "://" + location.host + SSH_API.terminal + search;
|
|
484
|
+
const base = hostAuthorityOf(streamBaseUrl);
|
|
485
|
+
if (base === void 0) return void 0;
|
|
486
|
+
return (base.protocol === "https:" ? "wss" : "ws") + "://" + base.authority + SSH_API.terminal + search;
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* The authority and scheme of a shell-owned Host base, or undefined when the
|
|
490
|
+
* value is absent, unparsable, or not a network origin this Host can serve
|
|
491
|
+
* (an application scheme could only resolve back to the page that has no
|
|
492
|
+
* socket transport in the first place).
|
|
493
|
+
* @param base - the raw `streamBaseUrl` as published by the transport hook.
|
|
494
|
+
*/
|
|
495
|
+
function hostAuthorityOf(base) {
|
|
496
|
+
if (base === void 0 || base === "") return void 0;
|
|
497
|
+
let url;
|
|
498
|
+
try {
|
|
499
|
+
url = new URL(base);
|
|
500
|
+
} catch {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return void 0;
|
|
504
|
+
if (url.username !== "" || url.password !== "") return void 0;
|
|
505
|
+
if (url.host === "") return void 0;
|
|
506
|
+
return {
|
|
507
|
+
protocol: url.protocol,
|
|
508
|
+
authority: url.host
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* The shell-owned Host authority the page is told to reach, read from the
|
|
513
|
+
* official transport hook. The global is written by the shell before any boot
|
|
514
|
+
* entry runs, so it is absent on every page it does not own.
|
|
515
|
+
* @param globals - the global object to read (defaults to the real one).
|
|
516
|
+
* @returns the published base URL, or undefined when the page carries none.
|
|
517
|
+
*/
|
|
518
|
+
function transportStreamBaseUrl(globals = globalThis) {
|
|
519
|
+
const published = globals.__DSH_TRANSPORT__?.streamBaseUrl;
|
|
520
|
+
return typeof published === "string" ? published : void 0;
|
|
473
521
|
}
|
|
474
522
|
/**
|
|
475
523
|
* Schemes a network page can be delivered with. Every other scheme belongs to
|
|
@@ -733,7 +781,7 @@ window.__ModuleLoader__.load({
|
|
|
733
781
|
}
|
|
734
782
|
/** One terminal socket over either an alias (open) or a session id (attach). */
|
|
735
783
|
terminalSocket(search) {
|
|
736
|
-
const target = terminalSocketUrl(window.location, search);
|
|
784
|
+
const target = terminalSocketUrl(window.location, search, transportStreamBaseUrl());
|
|
737
785
|
if (target === void 0) return failedTerminal(tt("terminal.noWebSocket"));
|
|
738
786
|
let socket;
|
|
739
787
|
try {
|
|
@@ -16367,7 +16415,7 @@ window.__ModuleLoader__.load({
|
|
|
16367
16415
|
/** The building package's version, when the bundle carries it. */
|
|
16368
16416
|
function bakedVersion() {
|
|
16369
16417
|
try {
|
|
16370
|
-
return "0.4.
|
|
16418
|
+
return "0.4.6";
|
|
16371
16419
|
} catch {
|
|
16372
16420
|
return;
|
|
16373
16421
|
}
|