@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 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: e477f468d33c808362bab0fd246dc6eb2bfefb62
6
- README.zh.md: d37b585d6e6a18f1a9bb22e8d9e223fb39b91076
5
+ README.md: 9031d9df52f51011cc951cba61665da91b42c65b
6
+ README.zh.md: ddae66eb2e06138a8c9157dcfd07da00acdba0f1
package/README.md CHANGED
@@ -1,8 +1,21 @@
1
- # dsh-ssh — Remote SSH operations plugin for DSH
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
- Built on the capability list of [badseal/ssh-skill](https://github.com/badseal/ssh-skill), a remote SSH plugin tailored for DeepSeek Harness (DSH): a persistent connection pool inside the Host process + a Web GUI host-management panel + a Web terminal + Agent tools, all implemented through the official NPM SDK without modifying DSH source.
5
+ <p align="center">
6
+ <img src="https://img.shields.io/npm/v/@linxin666/dsh-ssh?style=flat-square" alt="Version">
7
+ &nbsp;
8
+ <img src="https://img.shields.io/badge/DSH-%3E%3D0.2.0--rc.2-4c6ef5?style=flat-square&amp;labelColor=454a54" alt="DSH">
9
+ &nbsp;
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 — 远程 SSH 运维插件(DSH 版 ssh-skill)
1
+ # dsh-ssh · DeepSeek Harness (DSH) 网页版 SSH 终端、SFTP 文件传输与集群运维插件
2
2
 
3
3
  [English](README.md) | 中文
4
4
 
5
- 基于 [badseal/ssh-skill](https://github.com/badseal/ssh-skill) 的能力清单,为 DeepSeek Harness(DSH)定制的远程 SSH 插件:Host 进程内的持久连接池 + Web GUI 主机管理面板 + Web 终端 + Agent 工具,全部通过官方 NPM SDK 实现,不修改 DSH 源码。
5
+ <p align="center">
6
+ <img src="https://img.shields.io/npm/v/@linxin666/dsh-ssh?style=flat-square" alt="Version">
7
+ &nbsp;
8
+ <img src="https://img.shields.io/badge/DSH-%3E%3D0.2.0--rc.2-4c6ef5?style=flat-square&amp;labelColor=454a54" alt="DSH">
9
+ &nbsp;
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(应用外壳只转发 HTTP),请改用浏览器打开的 Web 界面使用终端。",
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 forwards HTTP only). Open the Web UI in a browser to use the terminal.",
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 delivered by an application on this machine (the official DSH
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 blank authority is treated the same as an application scheme: there is
464
- * nothing to dial, and the caller gets the actionable reason instead of a
465
- * socket that can only fail.
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.4";
16418
+ return "0.4.6";
16371
16419
  } catch {
16372
16420
  return;
16373
16421
  }