@zhangfengshun/dsh-remote-ssh 2.1.6 → 2.1.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/CHANGELOG.md +43 -0
- package/README.md +1 -1
- package/README_EN.md +67 -0
- package/lib/client.js +33 -0
- package/lib/index.js +35 -16
- package/package.json +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
本文件的版本号与 `package.json` 的 `version` 保持一致。每个版本对应一个 Cordis Package 快照(`pkg-N`)。
|
|
4
4
|
|
|
5
|
+
## [2.1.7] — 兼容 DSH Desktop ≥ 2.0.4:polyfill __DSH_MODULES__ + 可靠性/安全修复
|
|
6
|
+
### 修复
|
|
7
|
+
- **__DSH_MODULES__ polyfill**:better-sidebar 0.15+ 的懒加载 chunk(编辑器等)依赖 `globalThis.__DSH_MODULES__`,而 DSH Desktop ≥ 2.0.4 的 shell 不再挂载该全局,导致侧边栏面板打不开("chunk ... client module system unavailable")。Client 半边启动时用内核 `modules` 服务补挂该全局(better-sidebar 已注入则保持原值),并带定期兜底重试。
|
|
8
|
+
- **持久会话 stderr 排空**:SSH 会话的 stderr 管道此前无人消费,写满后远端 ssh 进程阻塞、会话假死;现在持续排空并保留最近 64KB,会话意外关闭时报错会附带最近 stderr 尾部,便于排查断连原因。
|
|
9
|
+
- **修复 `remote_ssh_grep` 命令注入**:`--include` 参数此前未做 shell 转义,可通过构造参数在远端执行任意命令;现已与其它参数一样经 `shellQuote` 引用。
|
|
10
|
+
- **删除连接配置时级联清理**:删除 profile 时同步删除其远程工作区的原生注册与本地镜像目录(复用统一的 `cleanupWorkspace`),不再留下指向已删配置的孤儿工作区。
|
|
11
|
+
- **密钥路径支持 `~` 展开**:`keyPath` 按 `~`、`~/` 前缀展开为绝对路径,与设置界面占位符(`~/.ssh/id_rsa`)行为一致。
|
|
12
|
+
- **会话池 key 纳入认证信息**:修改连接配置的 keyPath/authMethod 后立即重建 SSH 会话,不再复用旧连接。
|
|
13
|
+
- 清理每次文件请求的 `console.log` 调试输出(改走 `ctx.logger.debug`);`localToRemote` 对镜像目录之外的路径不再静默拼出错误远程路径。
|
|
14
|
+
- 文档:补全 2.1.0 以来缺失的 CHANGELOG 条目;README 安装命令版本号同步为 2.1.7。
|
|
15
|
+
|
|
16
|
+
## [2.1.6] — P0 打磨:远程下载 / 流式上传完善 / 远程 Git / 重连
|
|
17
|
+
### 修复 / 改进
|
|
18
|
+
- **远程文件下载与媒体预览**:拦截 better-sidebar 的 `/sidebar/file`(同路径 exact 路由优先),远程工作区中的文件直接从远端 base64 拉回(上限 64MB),本地照旧。
|
|
19
|
+
- **流式上传**:`/remote-ssh/upload` 把请求体直接管道到远端 `cat > target`(二进制安全、恒定内存),请求中断时终止远端进程。
|
|
20
|
+
- **远程 Git 面板**:拦截 `/sidebar/api/git.*`(status/diff/log/branch/commit-diff/show/stage/unstage/commit/checkout/discard/revert/cherry-pick),远程工作区中在远端目录执行同语义 git 命令。
|
|
21
|
+
- **SSH 失败修复建议**:端口转发占用 / 公钥认证失败 / 连接拒绝 / 超时 / 域名解析失败翻译为带修复建议的中文提示。
|
|
22
|
+
|
|
23
|
+
## [2.1.5] — 同步 better-sidebar 0.15+ 上传到远程工作区
|
|
24
|
+
### 修复
|
|
25
|
+
- better-sidebar 0.15+ 的上传 UI 直连 `/sidebar/upload`,远程工作区镜像目录会导致文件只落在本地镜像。Client 半边包装 `fetch`:目标目录位于远程工作区镜像内时把上传重定向到 `/remote-ssh/upload`,经 SSH 直接写远端。
|
|
26
|
+
|
|
27
|
+
## [2.1.4] — 修复远程工作区标题卡在镜像目录名
|
|
28
|
+
### 修复
|
|
29
|
+
- `workspaceRegistry.create` 只对新建记录应用 title,旧工作区(早期版本未传 title)标题卡在镜像目录名(如 `wmt3tev5cdfge`)。创建时显式 `setTitle`,并新增启动自愈 `healWorkspaceTitles`(仅在标题等于镜像目录 basename 时修复,不覆盖用户自定义标题)。
|
|
30
|
+
|
|
31
|
+
## [2.1.3] — 远程工作区标题默认取远程路径最后一段
|
|
32
|
+
### 改进
|
|
33
|
+
- 创建远程工作区时的默认标题改为远程路径最后一段目录名(如 `~/run/zfs/codex/DSH_Test → DSH_Test`),取不到有效段时回退为 `连接名:远程路径`;原生工作区标题带 🌐 前缀。
|
|
34
|
+
|
|
35
|
+
## [2.1.2] — 修复 ssh config RemoteForward 端口占用导致连接失败
|
|
36
|
+
### 修复
|
|
37
|
+
- 插件内部连接(文件读写 / 工具 / 同步)追加 `ClearAllForwardings=yes`;shell wrapper 追加 `ExitOnForwardFailure=no`。`~/.ssh/config` 里的 `RemoteForward` 端口被上次会话占用且 `ExitOnForwardFailure yes` 时不再导致连接直接失败。
|
|
38
|
+
- 把 SSH 常见失败(端口转发占用 / 公钥认证失败 / 连接拒绝 / 超时 / 域名解析失败)翻译成带修复建议的中文提示。
|
|
39
|
+
|
|
40
|
+
## [2.1.1] — 新增英文 README
|
|
41
|
+
### 文档
|
|
42
|
+
- 新增 README_EN.md,中英文版本互链。
|
|
43
|
+
|
|
44
|
+
## [2.1.0] — README 拆分语言版本
|
|
45
|
+
### 文档
|
|
46
|
+
- 移除 README 内联双语,拆分为 README.md(中文)与 README_EN.md(英文)。
|
|
47
|
+
|
|
5
48
|
## [2.0.1] — 修复远程检测:从 payload.path 也查找 .remote-ssh.json
|
|
6
49
|
### 修复
|
|
7
50
|
- **sessionCwd 为空时无法检测远程工作区**:客户端请求有时只带 `payload.path` 不带 `cwd`,而 `ctx.sessions.get(sessionId)` 可能返回 undefined(session 未创建或 ID 不匹配)。现改为:先尝试 `sessionCwd`,再尝试 `payload.path`,两者都会检查 `.remote-ssh.json`。
|
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
## 安装
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
dsh plugin --profile <name> add @zhangfengshun/dsh-remote-ssh@2.1.
|
|
20
|
+
dsh plugin --profile <name> add @zhangfengshun/dsh-remote-ssh@2.1.7
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
> 安装后需**重启 DSH**。`@zhangfengshun/dsh-remote-ssh` 必须在 bundles 列表中排在 `dsh-better-sidebar` **之后**。
|
package/README_EN.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# @zhangfengshun/dsh-remote-ssh
|
|
2
|
+
|
|
3
|
+
English | [中文](./README.md)
|
|
4
|
+
|
|
5
|
+
A **DSH** plugin like **VSCode Remote-SSH**: connect to remote HPC / servers via SSH, and directly operate remote files and terminals within DSH's built-in **Files** and **Terminal** sidebar tabs.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
| Feature | Description |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| 🔌 SSH Connection | Key / password auth, ProxyJump bastion, one-click import from `~/.ssh/config` |
|
|
12
|
+
| 📂 Remote Files | Built-in **Files** tab reads/writes remote files directly via SSH — no sync needed |
|
|
13
|
+
| 💻 Remote Terminal | Built-in **Terminal** tab auto-detects remote workspaces, opens SSH interactive shell |
|
|
14
|
+
| 🌐 Remote Workspace | Select a remote directory to create a native workspace, one-click enter |
|
|
15
|
+
| 🤖 Model Tools | 12 `remote_ssh_*` tools, session-aware with auto-filled connection params |
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
dsh plugin --profile <name> add @zhangfengshun/dsh-remote-ssh@2.1.7
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
> **Restart DSH** after installation. `@zhangfengshun/dsh-remote-ssh` must come **after** `dsh-better-sidebar` in the bundles list.
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
1. **Settings → 🖥️ Remote SSH** → Add a connection (host/port/user/key) → Click "Test Connection"
|
|
28
|
+
2. **Add Workspace** → Choose "Select Remote Directory…" → Pick a connection → Browse and select
|
|
29
|
+
3. Open the built-in **Files** tab → Remote files shown directly, edits save back to remote
|
|
30
|
+
4. Open the built-in **Terminal** tab → Auto SSH to remote host (key auth only)
|
|
31
|
+
|
|
32
|
+
## Model Tools
|
|
33
|
+
|
|
34
|
+
| Tool | Purpose |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| `remote_ssh_profiles` | List saved connections + current session's remote workspace context |
|
|
37
|
+
| `remote_ssh_exec` | Execute remote command |
|
|
38
|
+
| `remote_ssh_ls` | List remote directory |
|
|
39
|
+
| `remote_ssh_cat` | Read remote file |
|
|
40
|
+
| `remote_ssh_write` | Write remote file |
|
|
41
|
+
| `remote_ssh_grep` | Search remote file contents |
|
|
42
|
+
| `remote_ssh_glob` | Find remote files by glob |
|
|
43
|
+
| `remote_ssh_mkdir` | Create remote directory |
|
|
44
|
+
| `remote_ssh_delete` | Delete remote file/directory |
|
|
45
|
+
| `remote_ssh_move` | Move/rename |
|
|
46
|
+
| `remote_ssh_sync` | Sync remote to local mirror |
|
|
47
|
+
| `remote_ssh_push` | Push local mirror back to remote |
|
|
48
|
+
|
|
49
|
+
In a remote-workspace session, `profileId` and other connection params can be omitted.
|
|
50
|
+
|
|
51
|
+
## How It Works
|
|
52
|
+
|
|
53
|
+
The plugin registers 4 exact routes (`/sidebar/api/fs.tree`, `fs.read`, `fs.write`, `fs.search`) that intercept better-sidebar's prefix route. When the session cwd contains `.remote-ssh.json`, requests go through SSH; otherwise local fs. The client sees local mirror paths — the Host transparently translates them to remote paths.
|
|
54
|
+
|
|
55
|
+
A shell wrapper (`~/.dsh/remote-ssh/dsh-remote-shell[.cmd]`) detects the workspace's `.remote-ssh.json` and auto-launches `ssh -tt`, making the built-in **Terminal** tab transparently connect to remote.
|
|
56
|
+
|
|
57
|
+
## ❤️ Happy Qixi
|
|
58
|
+
|
|
59
|
+
This project is a Qixi Festival gift for **zhangyi**.
|
|
60
|
+
|
|
61
|
+
May it connect us as closely as it connects to distant supercomputers. Happy Qixi ❤️
|
|
62
|
+
|
|
63
|
+
—— August 18, 2026
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
|
|
67
|
+
[MIT](./LICENSE)
|
package/lib/client.js
CHANGED
|
@@ -1063,6 +1063,39 @@ window.__ModuleLoader__.load({
|
|
|
1063
1063
|
var slots = ctx.slots;
|
|
1064
1064
|
var locale = ctx.locale;
|
|
1065
1065
|
|
|
1066
|
+
// 兼容 DSH Desktop ≥2.0.4:shell 不再把客户端模块系统挂到
|
|
1067
|
+
// globalThis.__DSH_MODULES__,而 dsh-better-sidebar 0.15+ 的懒加载
|
|
1068
|
+
// chunk(editor 等)会回退到该全局——缺失时侧边栏面板打不开、报
|
|
1069
|
+
// "chunk ... client module system unavailable"。这里用内核提供的
|
|
1070
|
+
// modules 服务补挂该全局;better-sidebar 已注入则保持原值。
|
|
1071
|
+
try {
|
|
1072
|
+
if (typeof globalThis !== "undefined") {
|
|
1073
|
+
var fallbackModules = null;
|
|
1074
|
+
try { if (ctx.modules && typeof ctx.modules.import === "function") fallbackModules = ctx.modules; } catch (e) {}
|
|
1075
|
+
if (!fallbackModules && ctx.get && typeof ctx.get === "function") {
|
|
1076
|
+
try { fallbackModules = ctx.get("modules"); } catch (e) { fallbackModules = null; }
|
|
1077
|
+
}
|
|
1078
|
+
// 定期兜底:better-sidebar 若把本插件应用时间之后的模块系统清掉,也补回。
|
|
1079
|
+
if (globalThis.__DSH_MODULES__ === undefined && fallbackModules === null) {
|
|
1080
|
+
var waitModules = 0;
|
|
1081
|
+
var waitTimer = setInterval(function () {
|
|
1082
|
+
waitModules += 1;
|
|
1083
|
+
try {
|
|
1084
|
+
var ms = (ctx.get && typeof ctx.get === "function") ? ctx.get("modules") : null;
|
|
1085
|
+
if (ms && typeof ms.import === "function") {
|
|
1086
|
+
globalThis.__DSH_MODULES__ = ms;
|
|
1087
|
+
clearInterval(waitTimer);
|
|
1088
|
+
}
|
|
1089
|
+
} catch (e) {}
|
|
1090
|
+
if (globalThis.__DSH_MODULES__ !== undefined || waitModules > 40) clearInterval(waitTimer);
|
|
1091
|
+
}, 250);
|
|
1092
|
+
}
|
|
1093
|
+
if (!globalThis.__DSH_MODULES__ && fallbackModules && typeof fallbackModules.import === "function") {
|
|
1094
|
+
globalThis.__DSH_MODULES__ = fallbackModules;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
} catch (e) {}
|
|
1098
|
+
|
|
1066
1099
|
// 注册中英文词典并绑定当前语言;locale 切换时 i18n.subscribe 触发组件重渲染。
|
|
1067
1100
|
if (locale) {
|
|
1068
1101
|
ctx.effect(function () { return locale.register(NS, { zh: zh, en: en }); }, "dsh-remote-ssh: dictionaries");
|
package/lib/index.js
CHANGED
|
@@ -176,6 +176,8 @@ class CommandSession {
|
|
|
176
176
|
this.sentinel = "DSHEOF" + Math.random().toString(36).slice(2, 14) + Math.random().toString(36).slice(2, 10) + "DSHEOF";
|
|
177
177
|
this.lastUsed = Date.now();
|
|
178
178
|
this.connectPromise = null;
|
|
179
|
+
/** 最近一段 stderr(防背压消费 + 会话关闭诊断)。 */
|
|
180
|
+
this.errBuf = "";
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
async connect() {
|
|
@@ -189,6 +191,7 @@ class CommandSession {
|
|
|
189
191
|
if (this.handle) { try { this.handle.terminate(); } catch (e) {} this.handle = null; }
|
|
190
192
|
this.alive = false;
|
|
191
193
|
this.buf = "";
|
|
194
|
+
this.errBuf = "";
|
|
192
195
|
this.current = null;
|
|
193
196
|
this.queue = [];
|
|
194
197
|
const argv = sshArgv(this.profile, "bash", false);
|
|
@@ -203,10 +206,16 @@ class CommandSession {
|
|
|
203
206
|
this.buf += chunk.toString("utf8");
|
|
204
207
|
this._check();
|
|
205
208
|
});
|
|
209
|
+
// stderr 必须消费:不挂监听会让管道缓冲填满、远端 ssh 写 stderr 阻塞(会话假死)。
|
|
210
|
+
// 保留最近 64KB 用于会话关闭时的诊断。
|
|
211
|
+
h.stderr.on("data", (chunk) => {
|
|
212
|
+
this.errBuf = (this.errBuf + chunk.toString("utf8")).slice(-65536);
|
|
213
|
+
});
|
|
206
214
|
const onEnd = () => {
|
|
207
215
|
this.alive = false;
|
|
208
|
-
|
|
209
|
-
|
|
216
|
+
const errMsg = "ssh 会话已关闭" + (this.errBuf.trim() ? ": " + this.errBuf.trim().slice(-300) : "");
|
|
217
|
+
if (this.current) { this.current.reject(new Error(errMsg)); this.current = null; }
|
|
218
|
+
while (this.queue.length) this.queue.shift().reject(new Error(errMsg));
|
|
210
219
|
};
|
|
211
220
|
h.stdout.on("end", onEnd);
|
|
212
221
|
h.stdout.on("error", onEnd);
|
|
@@ -283,7 +292,7 @@ function sshArgv(p, remoteCmd, tty) {
|
|
|
283
292
|
// 清除 ssh config 里的 RemoteForward/LocalForward,避免端口被占时连接直接失败。
|
|
284
293
|
opts.push("-o", "ClearAllForwardings=yes");
|
|
285
294
|
if (p.proxyJump) opts.push("-o", "ProxyJump=" + String(p.proxyJump));
|
|
286
|
-
if (p.authMethod === "key" && p.keyPath) opts.push("-i", p.keyPath);
|
|
295
|
+
if (p.authMethod === "key" && p.keyPath) opts.push("-i", expandSshPath(p.keyPath));
|
|
287
296
|
const target = String(p.user || "") + "@" + String(p.host || "");
|
|
288
297
|
const head = (p.authMethod === "password" && p.password) ? ["sshpass", "-p", String(p.password)] : [];
|
|
289
298
|
const argv = head.concat(opts, [target]);
|
|
@@ -388,7 +397,7 @@ async function remoteGrep(runner, p, pattern, path, opts) {
|
|
|
388
397
|
const target = path || p.remoteRoot || "~";
|
|
389
398
|
const parts = ["grep", "-rnIE"];
|
|
390
399
|
if (opts.ignoreCase) parts.push("-i");
|
|
391
|
-
if (opts.include) parts.push("--include=" + String(opts.include));
|
|
400
|
+
if (opts.include) parts.push("--include=" + shellQuote(String(opts.include)));
|
|
392
401
|
parts.push("--", shellQuote(pattern), shellQuotePath(target));
|
|
393
402
|
const cmd = parts.join(" ");
|
|
394
403
|
const r = await runner(p, cmd, undefined, MAX_BYTES);
|
|
@@ -653,7 +662,8 @@ function apply(ctx, config) {
|
|
|
653
662
|
const sessions = new Map(); // profileKey -> CommandSession
|
|
654
663
|
const SESSION_IDLE_MS = 10 * 60 * 1000;
|
|
655
664
|
function profileKey(p) {
|
|
656
|
-
|
|
665
|
+
// 认证信息变化时应重建会话:修改 keyPath/authMethod 后旧连接不能继续复用。
|
|
666
|
+
return String(p.id) + "|" + String(p.host) + ":" + String(p.port || 22) + "|" + String(p.user || "") + "|" + String(p.authMethod || "key") + "|" + String(p.keyPath || "");
|
|
657
667
|
}
|
|
658
668
|
function getSession(p) {
|
|
659
669
|
const key = profileKey(p);
|
|
@@ -782,6 +792,18 @@ function apply(ctx, config) {
|
|
|
782
792
|
return base.replace(/\/+$/, "") + "/" + String(path);
|
|
783
793
|
}
|
|
784
794
|
|
|
795
|
+
/** 清理远程工作区的原生注册与本地镜像目录(删除工作区或级联删除 profile 时调用)。 */
|
|
796
|
+
async function cleanupWorkspace(ws) {
|
|
797
|
+
if (!ws || !ws.mirrorPath) return;
|
|
798
|
+
if (workspaceRegistry) {
|
|
799
|
+
try {
|
|
800
|
+
const entity = await workspaceRegistry.resolveByPath(ws.mirrorPath);
|
|
801
|
+
if (entity) await workspaceRegistry.delete(entity.id);
|
|
802
|
+
} catch (e) {}
|
|
803
|
+
}
|
|
804
|
+
try { await rm(ws.mirrorPath, { recursive: true, force: true }); } catch (e) {}
|
|
805
|
+
}
|
|
806
|
+
|
|
785
807
|
const api = {
|
|
786
808
|
listProfiles: async () => ({ ok: true, profiles: settingsFace.read().profiles }),
|
|
787
809
|
/** 读取本机 ~/.ssh/config 发现的主机(借鉴 Yan-Zero dsh-remote-ssh)。 */
|
|
@@ -816,9 +838,12 @@ function apply(ctx, config) {
|
|
|
816
838
|
deleteProfile: async (args) => {
|
|
817
839
|
const id = args && args.id;
|
|
818
840
|
const profiles = settingsFace.read().profiles.filter((p) => p.id !== id);
|
|
841
|
+
const orphaned = settingsFace.read().workspaces.filter((w) => w.profileId === id);
|
|
819
842
|
const workspaces = settingsFace.read().workspaces.filter((w) => w.profileId !== id);
|
|
820
843
|
await settingsFace.updateProfiles(profiles);
|
|
821
844
|
await settingsFace.updateWorkspaces(workspaces);
|
|
845
|
+
// 级联清理:相关远程工作区的原生注册与本地镜像目录一并删除,不留孤儿。
|
|
846
|
+
for (const ws of orphaned) await cleanupWorkspace(ws);
|
|
822
847
|
return { ok: true, profiles: profiles, workspaces: workspaces };
|
|
823
848
|
},
|
|
824
849
|
testConnection: async (args) => {
|
|
@@ -891,15 +916,7 @@ function apply(ctx, config) {
|
|
|
891
916
|
const ws = all.find((w) => w.id === id);
|
|
892
917
|
const workspaces = all.filter((w) => w.id !== id);
|
|
893
918
|
await settingsFace.updateWorkspaces(workspaces);
|
|
894
|
-
|
|
895
|
-
if (workspaceRegistry) {
|
|
896
|
-
try {
|
|
897
|
-
const entity = await workspaceRegistry.resolveByPath(ws.mirrorPath);
|
|
898
|
-
if (entity) await workspaceRegistry.delete(entity.id);
|
|
899
|
-
} catch (e) {}
|
|
900
|
-
}
|
|
901
|
-
try { await rm(ws.mirrorPath, { recursive: true, force: true }); } catch (e) {}
|
|
902
|
-
}
|
|
919
|
+
await cleanupWorkspace(ws);
|
|
903
920
|
return { ok: true, workspaces: workspaces };
|
|
904
921
|
},
|
|
905
922
|
updateWorkspace: async (args) => {
|
|
@@ -1480,7 +1497,9 @@ function apply(ctx, config) {
|
|
|
1480
1497
|
if (rel.toLowerCase().startsWith(mc.toLowerCase())) {
|
|
1481
1498
|
rel = rel.slice(mc.length);
|
|
1482
1499
|
} else {
|
|
1483
|
-
//
|
|
1500
|
+
// 不在镜像目录下:剥掉可能的前导斜杠与盘符,按相对路径处理(此前会静默拼出错误路径)。
|
|
1501
|
+
rel = rel.replace(/^[A-Za-z]:/, "").replace(/^\/+/, "");
|
|
1502
|
+
try { ctx.logger?.debug("[dsh-remote-ssh] localToRemote: path outside mirror, treated as relative: " + localPath); } catch (e) {}
|
|
1484
1503
|
}
|
|
1485
1504
|
if (rel.startsWith("/")) rel = rel.slice(1);
|
|
1486
1505
|
if (!rel) return remoteBase;
|
|
@@ -1634,7 +1653,7 @@ function apply(ctx, config) {
|
|
|
1634
1653
|
remoteInfo = readRemoteInfoSync(payload.path);
|
|
1635
1654
|
remoteBase = payload.path;
|
|
1636
1655
|
}
|
|
1637
|
-
|
|
1656
|
+
try { ctx.logger?.debug("[dsh-remote-ssh] intercept " + method + ": sessionId=" + sessionId + " cwd=" + sessionCwd + " path=" + (payload && payload.path) + " remote=" + (remoteInfo ? "YES" : "NO")); } catch (e) {}
|
|
1638
1657
|
let result;
|
|
1639
1658
|
if (remoteInfo && remoteInfo.keyPath && remoteInfo.host && remoteInfo.user) {
|
|
1640
1659
|
// ---- 远程工作区:走 SSH ----
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhangfengshun/dsh-remote-ssh",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.7",
|
|
4
4
|
"description": "DSH web plugin: VSCode Remote-SSH-like remote development (SSH to supercomputers/servers, remote workspace, file explorer, integrated terminal), integrated with dsh-better-sidebar and DSH settings.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"client": {
|
|
42
42
|
"inject": [
|
|
43
|
+
"@deepseek-ai/dsh-client-modules",
|
|
43
44
|
"@deepseek-ai/dsh-client-runtime",
|
|
44
45
|
"@deepseek-ai/dsh-client-locale",
|
|
45
46
|
"@deepseek-ai/dsh-client-ui-slots",
|
|
@@ -66,6 +67,7 @@
|
|
|
66
67
|
"lib/client.js",
|
|
67
68
|
"cordis.patch.yml",
|
|
68
69
|
"README.md",
|
|
70
|
+
"README_EN.md",
|
|
69
71
|
"LICENSE",
|
|
70
72
|
"CHANGELOG.md"
|
|
71
73
|
],
|