@zhangfengshun/dsh-remote-ssh 2.1.1 → 2.1.3

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.
Files changed (2) hide show
  1. package/lib/index.js +45 -3
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -279,6 +279,9 @@ function sshArgv(p, remoteCmd, tty) {
279
279
  opts.push("-o", "ConnectTimeout=15");
280
280
  opts.push("-o", "ServerAliveInterval=30");
281
281
  opts.push("-o", "ServerAliveCountMax=3");
282
+ // 插件内部连接(文件读写/工具/同步)不需要端口转发;
283
+ // 清除 ssh config 里的 RemoteForward/LocalForward,避免端口被占时连接直接失败。
284
+ opts.push("-o", "ClearAllForwardings=yes");
282
285
  if (p.proxyJump) opts.push("-o", "ProxyJump=" + String(p.proxyJump));
283
286
  if (p.authMethod === "key" && p.keyPath) opts.push("-i", p.keyPath);
284
287
  const target = String(p.user || "") + "@" + String(p.host || "");
@@ -808,7 +811,10 @@ function apply(ctx, config) {
808
811
  if (!p) return { ok: false, error: "未找到连接配置" };
809
812
  if (!a.remotePath) return { ok: false, error: "remotePath 为必填项" };
810
813
  const wsId = "w" + Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
811
- const title = a.title || (p.name + ":" + a.remotePath);
814
+ // 默认标题取远程路径最后一段目录名(如 ~/run/zfs/codex/DSH_Test DSH_Test);
815
+ // 取不到有效段(如 ~/ 或 /)时回退为 连接名:远程路径。
816
+ const lastSeg = String(a.remotePath).replace(/\/+$/, "").split("/").pop();
817
+ const title = a.title || (lastSeg && lastSeg !== "~" ? lastSeg : (p.name + ":" + a.remotePath));
812
818
  // 本地镜像目录:作为原生工作区注册进 workspaceRegistry,会话 cwd 即指向它。
813
819
  const mirrorDir = join(homedir(), ".dsh", "remote-workspaces", wsId);
814
820
  try {
@@ -832,8 +838,15 @@ function apply(ctx, config) {
832
838
  let workspaceId = null;
833
839
  if (workspaceRegistry) {
834
840
  try {
835
- const native = await workspaceRegistry.create(mirrorDir, "🌐 " + title);
841
+ const wsTitle = "🌐 " + title;
842
+ const native = await workspaceRegistry.create(mirrorDir, wsTitle);
836
843
  workspaceId = native.id;
844
+ // workspaceRegistry.create 仅首次创建时应用 title;已存在的旧工作区
845
+ // (早期版本未传 title,标题卡在镜像目录名,如 wmt3tev5cdfge)会被直接
846
+ // 返回而不更新。这里显式 setTitle,保证标题始终是远程路径最后一段目录名。
847
+ if (native && typeof native.setTitle === "function" && native.title !== wsTitle) {
848
+ await native.setTitle(wsTitle);
849
+ }
837
850
  } catch (e) {
838
851
  return { ok: false, error: "注册原生工作区失败: " + String(e && e.message ? e.message : e) };
839
852
  }
@@ -1348,7 +1361,8 @@ function apply(ctx, config) {
1348
1361
  " const j = JSON.parse(fs.readFileSync(info, 'utf8'));",
1349
1362
  " if (j.keyPath && j.keyPath !== '' && j.host && j.user) {",
1350
1363
  " const port = j.port || 22;",
1351
- " const args = ['-tt', '-o', 'StrictHostKeyChecking=no', '-p', String(port), '-i', j.keyPath, j.user + '@' + j.host];",
1364
+ " // ExitOnForwardFailure=no:ssh config 里的 RemoteForward 端口被占时终端仍能打开",
1365
+ " const args = ['-tt', '-o', 'StrictHostKeyChecking=no', '-o', 'ExitOnForwardFailure=no', '-p', String(port), '-i', j.keyPath, j.user + '@' + j.host];",
1352
1366
  " if (j.proxyJump) { args.splice(2, 0, '-J', j.proxyJump); }",
1353
1367
  " const ssh = spawn('ssh', args, { stdio: 'inherit' });",
1354
1368
  " ssh.on('exit', function(c) { process.exit(c == null ? 0 : c); });",
@@ -1389,6 +1403,34 @@ function apply(ctx, config) {
1389
1403
  }
1390
1404
  })();
1391
1405
 
1406
+ // ---- 启动自愈:把既有远程工作区的原生标题统一为「🌐 + 远程路径最后一段」----
1407
+ // 早期版本 create 工作区时未传 title,DSH 用镜像目录 basename(wsId,如 wmt3tev5cdfge)
1408
+ // 作标题;workspaceRegistry.create 只对新建记录应用 title,旧记录会被原样返回。
1409
+ // 这里仅在标题等于镜像目录 basename(明显是旧 bug 产物)时修复,不覆盖用户自定义标题。
1410
+ function healWorkspaceTitles() {
1411
+ const reg = ctx.get("workspaceRegistry");
1412
+ if (!reg || typeof reg.resolveByPath !== "function") return;
1413
+ const wsList = settingsFace.read().workspaces;
1414
+ for (const w of wsList) {
1415
+ if (!w || !w.mirrorPath || !w.remotePath) continue;
1416
+ const lastSeg = String(w.remotePath).replace(/\/+$/, "").split("/").pop();
1417
+ if (!lastSeg || lastSeg === "~" || lastSeg === ".") continue;
1418
+ const desired = "🌐 " + lastSeg;
1419
+ const base = String(w.mirrorPath).split(/[\\/]/).filter(Boolean).pop();
1420
+ (async () => {
1421
+ try {
1422
+ const ent = await reg.resolveByPath(w.mirrorPath);
1423
+ // 只在标题仍卡在镜像目录名(旧 bug)时才改,尊重用户自定义标题。
1424
+ if (ent && typeof ent.setTitle === "function" && (ent.title === base || ent.title === "🌐 " + base)) {
1425
+ await ent.setTitle(desired);
1426
+ }
1427
+ } catch (e) {}
1428
+ })();
1429
+ }
1430
+ }
1431
+ // settings 注入是异步的,稍作延迟确保 workspaces 已就绪。
1432
+ setTimeout(healWorkspaceTitles, 3000);
1433
+
1392
1434
  // ---- 拦截 better-sidebar 的 fs.* API:远程工作区走 SSH,本地走本地 fs ----
1393
1435
  // 注册更长前缀 /sidebar/api/fs. 优先于 better-sidebar 的 /sidebar/api 匹配。
1394
1436
  const READ_LIMIT = 524288;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhangfengshun/dsh-remote-ssh",
3
- "version": "2.1.1",
3
+ "version": "2.1.3",
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",