@hyzyn/dsh-tty 0.1.2 → 0.2.1
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.md +67 -9
- package/client.js +22 -22
- package/lib/index.d.ts +8 -2
- package/lib/index.js +223 -31
- package/lib/index.js.map +1 -1
- package/lib/ssh.d.ts +49 -0
- package/lib/ssh.js +232 -0
- package/lib/ssh.js.map +1 -0
- package/package.json +18 -12
- package/LICENSE +0 -21
package/lib/ssh.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { PassThrough } from 'node:stream';
|
|
2
|
+
export interface TermExit {
|
|
3
|
+
exitCode: number | null;
|
|
4
|
+
signal: string | null;
|
|
5
|
+
}
|
|
6
|
+
export interface TermHandle {
|
|
7
|
+
readonly kind: 'local' | 'ssh';
|
|
8
|
+
/** SSH 会话没有本地 pid,为 null。 */
|
|
9
|
+
readonly pid: number | null;
|
|
10
|
+
/** 输出流(flowing 模式消费;pause/resume 用于下行背压)。 */
|
|
11
|
+
readonly output: PassThrough;
|
|
12
|
+
/** 退出事实,恰好 resolve 一次。 */
|
|
13
|
+
readonly done: Promise<TermExit>;
|
|
14
|
+
write(data: string): Promise<unknown>;
|
|
15
|
+
resize(cols: number, rows: number): void;
|
|
16
|
+
terminate(): Promise<unknown>;
|
|
17
|
+
/** terminate 失败后的最后手段(本地 PTY:对顶层 shell 直接 SIGKILL)。 */
|
|
18
|
+
forceKill?(): void;
|
|
19
|
+
}
|
|
20
|
+
/** 内联 SSH 连接规格(ws 帧或连接簿条目共用)。 */
|
|
21
|
+
export interface SshSpec {
|
|
22
|
+
host: string;
|
|
23
|
+
port?: number;
|
|
24
|
+
username: string;
|
|
25
|
+
auth?: 'agent' | 'key' | 'password';
|
|
26
|
+
keyPath?: string;
|
|
27
|
+
passphrase?: string;
|
|
28
|
+
password?: string;
|
|
29
|
+
}
|
|
30
|
+
/** 连接簿条目(带名字,存 settings)。 */
|
|
31
|
+
export interface SshHostEntry extends SshSpec {
|
|
32
|
+
name: string;
|
|
33
|
+
}
|
|
34
|
+
export interface SshSpawnOptions {
|
|
35
|
+
term: string;
|
|
36
|
+
cols: number;
|
|
37
|
+
rows: number;
|
|
38
|
+
logger?: {
|
|
39
|
+
info(msg: string): void;
|
|
40
|
+
warn(msg: string): void;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** 展示用目标串:user@host(非默认端口时带 :port)。 */
|
|
44
|
+
export declare function sshTarget(spec: SshSpec): string;
|
|
45
|
+
/**
|
|
46
|
+
* 建立 SSH 连接并打开交互 shell channel,返回 TermHandle。
|
|
47
|
+
* 失败(连接超时/认证被拒/host 不可达)时 reject 带人类可读信息。
|
|
48
|
+
*/
|
|
49
|
+
export declare function spawnSsh(spec: SshSpec, options: SshSpawnOptions): Promise<TermHandle>;
|
package/lib/ssh.js
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hyzyn/dsh-tty — SSH 会话封装(方案 C:ssh2 原生集成)。
|
|
3
|
+
*
|
|
4
|
+
* 不经过本地 ssh 进程 / node-pty,直接用 ssh2 建立连接并开 shell channel,
|
|
5
|
+
* 包装成与本地 PTY 完全一致的 TermHandle 形状,TtyServer 无差别调度:
|
|
6
|
+
* input/resize/kill 上行,data/exit 下行,背压、环形缓冲、agent 工具全复用。
|
|
7
|
+
*
|
|
8
|
+
* 认证优先级由 spec.auth 决定:
|
|
9
|
+
* agent —— ssh-agent(SSH_AUTH_SOCK),最推荐,凭证不落盘
|
|
10
|
+
* key —— keyPath 私钥文件(~ 可省略 home),passphrase 可选
|
|
11
|
+
* password —— 密码认证,同时挂 keyboard-interactive(很多服务端只开这个)
|
|
12
|
+
* password / passphrase 支持 `env:VAR` 前缀从进程环境变量取值(配合
|
|
13
|
+
* dsh-env-manager 插件托管密钥,避免明文写入 settings 文件)。
|
|
14
|
+
*
|
|
15
|
+
* 主机密钥策略:当前版本为 accept-and-log——hostVerifier 记录 sha256 指纹
|
|
16
|
+
* 后无条件放行,每次连接都接受并记录,等同于
|
|
17
|
+
* `ssh -o StrictHostKeyChecking=no`(而非 accept-new):不做 known_hosts
|
|
18
|
+
* 钉扎,首次连接不询问,同一主机指纹变更也不会告警,因此存在被中间人
|
|
19
|
+
* (MITM)冒充的风险。loopback 围栏保证只有本机 Web GUI 能发起连接,
|
|
20
|
+
* 仅作为缓解;known_hosts 钉扎留作后续项。
|
|
21
|
+
*/
|
|
22
|
+
import { Client } from 'ssh2';
|
|
23
|
+
import { readFileSync } from 'node:fs';
|
|
24
|
+
import { homedir } from 'node:os';
|
|
25
|
+
import { join } from 'node:path';
|
|
26
|
+
import { PassThrough } from 'node:stream';
|
|
27
|
+
/** `env:VAR` 前缀从 process.env 取值;否则原样返回。 */
|
|
28
|
+
function resolveSecret(value) {
|
|
29
|
+
if (value === undefined)
|
|
30
|
+
return undefined;
|
|
31
|
+
if (!value.startsWith('env:'))
|
|
32
|
+
return value;
|
|
33
|
+
const name = value.slice(4);
|
|
34
|
+
const resolved = process.env[name];
|
|
35
|
+
if (resolved === undefined || resolved === '')
|
|
36
|
+
throw new Error(`环境变量未设置: ${name}`);
|
|
37
|
+
return resolved;
|
|
38
|
+
}
|
|
39
|
+
function expandHome(path) {
|
|
40
|
+
if (path === '~')
|
|
41
|
+
return homedir();
|
|
42
|
+
if (path.startsWith('~/'))
|
|
43
|
+
return join(homedir(), path.slice(2));
|
|
44
|
+
return path;
|
|
45
|
+
}
|
|
46
|
+
/** 展示用目标串:user@host(非默认端口时带 :port)。 */
|
|
47
|
+
export function sshTarget(spec) {
|
|
48
|
+
const port = spec.port ?? 22;
|
|
49
|
+
return `${spec.username}@${spec.host}${port === 22 ? '' : ':' + String(port)}`;
|
|
50
|
+
}
|
|
51
|
+
/* ------------------------------------------------------------------ *
|
|
52
|
+
* 连接与 channel 包装
|
|
53
|
+
* ------------------------------------------------------------------ */
|
|
54
|
+
function buildConnectConfig(spec) {
|
|
55
|
+
const auth = spec.auth ?? 'agent';
|
|
56
|
+
const base = {
|
|
57
|
+
host: spec.host,
|
|
58
|
+
port: spec.port ?? 22,
|
|
59
|
+
username: spec.username,
|
|
60
|
+
readyTimeout: 20000,
|
|
61
|
+
keepaliveInterval: 10000,
|
|
62
|
+
keepaliveCountMax: 3,
|
|
63
|
+
// accept-and-log:记录指纹后放行(见文件头策略说明)
|
|
64
|
+
hostHash: 'sha256',
|
|
65
|
+
};
|
|
66
|
+
if (auth === 'agent') {
|
|
67
|
+
base.agent = process.env.SSH_AUTH_SOCK;
|
|
68
|
+
}
|
|
69
|
+
else if (auth === 'key') {
|
|
70
|
+
if (typeof spec.keyPath !== 'string' || spec.keyPath.trim() === '') {
|
|
71
|
+
throw new Error('auth=key 需要 keyPath(私钥路径)');
|
|
72
|
+
}
|
|
73
|
+
base.privateKey = readFileSync(expandHome(spec.keyPath.trim()));
|
|
74
|
+
const passphrase = resolveSecret(spec.passphrase);
|
|
75
|
+
if (passphrase !== undefined)
|
|
76
|
+
base.passphrase = passphrase;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
const password = resolveSecret(spec.password);
|
|
80
|
+
if (password === undefined)
|
|
81
|
+
throw new Error('auth=password 需要 password(或 env:VAR 引用)');
|
|
82
|
+
base.password = password;
|
|
83
|
+
// 大量服务端(如部分路由器/堡垒机)只开 keyboard-interactive
|
|
84
|
+
base.tryKeyboard = true;
|
|
85
|
+
}
|
|
86
|
+
return base;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 建立 SSH 连接并打开交互 shell channel,返回 TermHandle。
|
|
90
|
+
* 失败(连接超时/认证被拒/host 不可达)时 reject 带人类可读信息。
|
|
91
|
+
*/
|
|
92
|
+
export async function spawnSsh(spec, options) {
|
|
93
|
+
const target = sshTarget(spec);
|
|
94
|
+
const logger = options.logger;
|
|
95
|
+
const conn = new Client();
|
|
96
|
+
const output = new PassThrough();
|
|
97
|
+
let exitCode = null;
|
|
98
|
+
let exitSignal = null;
|
|
99
|
+
let settleDone;
|
|
100
|
+
const done = new Promise((resolve) => {
|
|
101
|
+
settleDone = resolve;
|
|
102
|
+
});
|
|
103
|
+
let finished = false;
|
|
104
|
+
const finish = () => {
|
|
105
|
+
if (finished)
|
|
106
|
+
return;
|
|
107
|
+
finished = true;
|
|
108
|
+
try {
|
|
109
|
+
output.end();
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
/* 已结束 */
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
conn.end();
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
/* 已断开 */
|
|
119
|
+
}
|
|
120
|
+
settleDone({ exitCode, signal: exitSignal });
|
|
121
|
+
};
|
|
122
|
+
// 认证配置可能抛错(keyPath 读不到 / env 变量缺失)——先构造再连
|
|
123
|
+
const connectConfig = buildConnectConfig(spec);
|
|
124
|
+
connectConfig.hostVerifier = (hash) => {
|
|
125
|
+
logger?.info(`[dsh-tty] ssh ${target} host key sha256:${hash}(accept-and-log 放行)`);
|
|
126
|
+
return true;
|
|
127
|
+
};
|
|
128
|
+
const channel = await new Promise((resolve, reject) => {
|
|
129
|
+
let settled = false;
|
|
130
|
+
conn.on('ready', () => {
|
|
131
|
+
conn.shell({ term: options.term, cols: options.cols, rows: options.rows }, (error, ch) => {
|
|
132
|
+
settled = true;
|
|
133
|
+
if (error !== undefined && error !== null) {
|
|
134
|
+
conn.end();
|
|
135
|
+
reject(new Error(`shell channel 打开失败: ${error.message}`));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
resolve(ch);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
conn.on('error', (error) => {
|
|
142
|
+
if (!settled) {
|
|
143
|
+
settled = true;
|
|
144
|
+
reject(new Error(`SSH 连接失败(${target}): ${error.message}`));
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
logger?.warn(`[dsh-tty] ssh ${target} 连接错误: ${error.message}`);
|
|
148
|
+
finish();
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
conn.on('close', () => {
|
|
152
|
+
finish();
|
|
153
|
+
});
|
|
154
|
+
if (connectConfig.tryKeyboard === true) {
|
|
155
|
+
const password = connectConfig.password ?? '';
|
|
156
|
+
conn.on('keyboard-interactive', (_name, _instructions, _lang, _prompts, finishKb) => {
|
|
157
|
+
finishKb([password]);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
conn.connect(connectConfig);
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
channel.on('data', (chunk) => {
|
|
168
|
+
output.write(chunk);
|
|
169
|
+
});
|
|
170
|
+
channel.stderr.on('data', (chunk) => {
|
|
171
|
+
output.write(chunk);
|
|
172
|
+
});
|
|
173
|
+
channel.on('exit', (code, signal) => {
|
|
174
|
+
exitCode = typeof code === 'number' ? code : null;
|
|
175
|
+
exitSignal = typeof signal === 'string' ? signal : null;
|
|
176
|
+
});
|
|
177
|
+
channel.on('close', () => {
|
|
178
|
+
finish();
|
|
179
|
+
});
|
|
180
|
+
// 背压透传:TtyServer 暂停 PassThrough 时一并暂停上游 channel,
|
|
181
|
+
// 避免高速输出(cat 大文件)在 Node 侧无界堆积
|
|
182
|
+
const nativePause = output.pause.bind(output);
|
|
183
|
+
output.pause = () => {
|
|
184
|
+
try {
|
|
185
|
+
channel.pause();
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
/* channel 已关闭 */
|
|
189
|
+
}
|
|
190
|
+
return nativePause();
|
|
191
|
+
};
|
|
192
|
+
const nativeResume = output.resume.bind(output);
|
|
193
|
+
output.resume = () => {
|
|
194
|
+
try {
|
|
195
|
+
channel.resume();
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
/* channel 已关闭 */
|
|
199
|
+
}
|
|
200
|
+
return nativeResume();
|
|
201
|
+
};
|
|
202
|
+
logger?.info(`[dsh-tty] ssh 会话就绪: ${target}`);
|
|
203
|
+
return {
|
|
204
|
+
kind: 'ssh',
|
|
205
|
+
pid: null,
|
|
206
|
+
output,
|
|
207
|
+
done,
|
|
208
|
+
write: (data) => {
|
|
209
|
+
channel.write(data);
|
|
210
|
+
return Promise.resolve(true);
|
|
211
|
+
},
|
|
212
|
+
resize: (cols, rows) => {
|
|
213
|
+
try {
|
|
214
|
+
channel.setWindow(rows, cols, 0, 0);
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
/* channel 已关闭 */
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
terminate: () => {
|
|
221
|
+
try {
|
|
222
|
+
channel.close();
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
/* 已关闭 */
|
|
226
|
+
}
|
|
227
|
+
finish();
|
|
228
|
+
return Promise.resolve(true);
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=ssh.js.map
|
package/lib/ssh.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssh.js","sourceRoot":"","sources":["../src/ssh.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyzyn/dsh-tty",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "DSH Web GUI 的终端面板插件:侧边栏「终端」大弹窗,xterm.js + PTY 全交互终端(路线1)。",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "DSH Web GUI 的终端面板插件:侧边栏「终端」大弹窗,xterm.js + PTY 全交互终端(路线1),ssh2 原生 SSH 远程连接(方案 C)。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=22.19.0"
|
|
@@ -25,17 +25,22 @@
|
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@deepseek-ai/dsh-tools": "0.1.0-rc.7",
|
|
29
28
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
30
29
|
"@hyzyn/dsh-kit": "^0.1.0",
|
|
30
|
+
"ssh2": "^1.17.0",
|
|
31
31
|
"ws": "^8.18.0"
|
|
32
32
|
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"@deepseek-ai/dsh-tools": "^0.1.1-rc.2"
|
|
35
|
+
},
|
|
33
36
|
"devDependencies": {
|
|
34
37
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
38
|
+
"@deepseek-ai/dsh-tools": "0.1.1-rc.2",
|
|
35
39
|
"@deepseek-ai/dsh-host-webserver": "0.1.0-rc.7",
|
|
36
40
|
"@deepseek-ai/dsh-subprocess": "0.1.0-rc.7",
|
|
37
41
|
"@deepseek-ai/dsh-subprocess-local": "0.1.0-rc.7",
|
|
38
42
|
"@types/node": "^22.20.0",
|
|
43
|
+
"@types/ssh2": "^1.15.5",
|
|
39
44
|
"@types/ws": "^8.5.12",
|
|
40
45
|
"@xterm/addon-fit": "^0.10.0",
|
|
41
46
|
"@xterm/addon-search": "^0.16.0",
|
|
@@ -44,6 +49,15 @@
|
|
|
44
49
|
"esbuild": "^0.24.0",
|
|
45
50
|
"typescript": "~5.7.2"
|
|
46
51
|
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsc -p tsconfig.json && node scripts/build-client.mjs",
|
|
54
|
+
"typecheck": "tsc --noEmit",
|
|
55
|
+
"probe": "node scripts/probe.mjs",
|
|
56
|
+
"integration": "node scripts/integration.mjs",
|
|
57
|
+
"live": "node scripts/live.mjs",
|
|
58
|
+
"tui": "node scripts/tui-smoke.mjs",
|
|
59
|
+
"ssh-smoke": "node scripts/ssh-smoke.mjs"
|
|
60
|
+
},
|
|
47
61
|
"files": [
|
|
48
62
|
"lib",
|
|
49
63
|
"client.js",
|
|
@@ -62,13 +76,5 @@
|
|
|
62
76
|
},
|
|
63
77
|
"publishConfig": {
|
|
64
78
|
"access": "public"
|
|
65
|
-
},
|
|
66
|
-
"scripts": {
|
|
67
|
-
"build": "tsc -p tsconfig.json && node scripts/build-client.mjs",
|
|
68
|
-
"typecheck": "tsc --noEmit",
|
|
69
|
-
"probe": "node scripts/probe.mjs",
|
|
70
|
-
"integration": "node scripts/integration.mjs",
|
|
71
|
-
"live": "node scripts/live.mjs",
|
|
72
|
-
"tui": "node scripts/tui-smoke.mjs"
|
|
73
79
|
}
|
|
74
|
-
}
|
|
80
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 dsh-plugin-kit contributors
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|