@linxin666/dsh-ssh 0.3.5 → 0.3.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/lib/client.js +109 -9
- package/lib/client.js.map +1 -1
- package/lib/index.js +58 -10
- package/lib/types/client/api.d.ts +7 -0
- package/lib/types/client/locales.d.ts +5 -0
- package/lib/types/engine/connection-pool.d.ts +7 -2
- package/lib/types/engine/pty.d.ts +2 -2
- package/lib/types/engine.d.ts +3 -3
- package/lib/types/protocol.d.ts +11 -0
- package/package.json +1 -1
- package/src/client/api.ts +11 -0
- package/src/client/locales.ts +10 -0
- package/src/client/panel/TerminalTab.tsx +61 -1
- package/src/engine/connection-pool.ts +30 -4
- package/src/engine/pty.ts +8 -3
- package/src/engine.ts +8 -3
- package/src/protocol.ts +7 -0
- package/src/routes.ts +33 -2
package/src/routes.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { tmpdir } from 'node:os'
|
|
|
15
15
|
import { randomBytes } from 'node:crypto'
|
|
16
16
|
import { WebSocket, WebSocketServer } from 'ws'
|
|
17
17
|
import type { WebRoute, WebUpgradeRoute } from '@deepseek-ai/dsh-host-webserver'
|
|
18
|
-
import type { SshEngine, ShellSession } from './engine.ts'
|
|
18
|
+
import type { SshEngine, ShellSession, KeyboardInteractiveHandler } from './engine.ts'
|
|
19
19
|
import { readJsonBody, writeJson } from './http.ts'
|
|
20
20
|
import { isLoopbackRequest } from './loopback.ts'
|
|
21
21
|
import { SSH_API, type HostPayload, type TerminalClientFrame, type TerminalServerFrame } from './protocol.ts'
|
|
@@ -466,6 +466,7 @@ const maxUploadBytes = deps.maxUploadBytes ?? MAX_UPLOAD_BYTES
|
|
|
466
466
|
let session: ShellSession | undefined
|
|
467
467
|
let closed = false
|
|
468
468
|
let paused = false
|
|
469
|
+
let pendingAuthFinish: ((responses: string[]) => void) | undefined
|
|
469
470
|
// Resume the shell once the socket's send buffer drains below the
|
|
470
471
|
// low-water mark (transport backpressure).
|
|
471
472
|
const resume = (): void => {
|
|
@@ -483,14 +484,38 @@ const maxUploadBytes = deps.maxUploadBytes ?? MAX_UPLOAD_BYTES
|
|
|
483
484
|
}
|
|
484
485
|
}
|
|
485
486
|
const closeSession = (): void => {
|
|
487
|
+
if (pendingAuthFinish !== undefined) {
|
|
488
|
+
const fn = pendingAuthFinish
|
|
489
|
+
pendingAuthFinish = undefined
|
|
490
|
+
try { fn([]) } catch { /* ignore */ }
|
|
491
|
+
}
|
|
486
492
|
const opened = session
|
|
487
493
|
session = undefined
|
|
488
494
|
if (opened !== undefined) opened.close()
|
|
489
495
|
}
|
|
496
|
+
|
|
497
|
+
const onKeyboardInteractive: KeyboardInteractiveHandler = (name, instructions, _lang, prompts, finish) => {
|
|
498
|
+
const entry = store.find(alias)
|
|
499
|
+
// Auto-answer password if configured and prompt is a password request
|
|
500
|
+
if (entry?.auth.kind === 'password' && entry.auth.password !== undefined && prompts.length > 0 && prompts.every(p => /password/i.test(p.prompt))) {
|
|
501
|
+
const password = entry.auth.password
|
|
502
|
+
finish(prompts.map(() => password))
|
|
503
|
+
return
|
|
504
|
+
}
|
|
505
|
+
// Interactive 2FA prompt sent to the client terminal
|
|
506
|
+
pendingAuthFinish = finish
|
|
507
|
+
sendFrame({
|
|
508
|
+
type: 'auth_prompt',
|
|
509
|
+
name,
|
|
510
|
+
instructions,
|
|
511
|
+
prompts: prompts.map(p => ({ prompt: p.prompt, echo: p.echo })),
|
|
512
|
+
})
|
|
513
|
+
}
|
|
514
|
+
|
|
490
515
|
engine.openShell(alias, {
|
|
491
516
|
cols: Number.isFinite(cols) ? cols : 80,
|
|
492
517
|
rows: Number.isFinite(rows) ? rows : 24,
|
|
493
|
-
}).then((opened) => {
|
|
518
|
+
}, onKeyboardInteractive).then((opened) => {
|
|
494
519
|
if (ws.readyState !== WebSocket.OPEN) {
|
|
495
520
|
opened.close()
|
|
496
521
|
return
|
|
@@ -519,6 +544,12 @@ const maxUploadBytes = deps.maxUploadBytes ?? MAX_UPLOAD_BYTES
|
|
|
519
544
|
session?.send(frame.data)
|
|
520
545
|
} else if (frame.type === 'resize') {
|
|
521
546
|
session?.resize(Math.max(2, frame.cols), Math.max(1, frame.rows))
|
|
547
|
+
} else if (frame.type === 'auth_response') {
|
|
548
|
+
if (pendingAuthFinish !== undefined) {
|
|
549
|
+
const fn = pendingAuthFinish
|
|
550
|
+
pendingAuthFinish = undefined
|
|
551
|
+
fn(frame.responses)
|
|
552
|
+
}
|
|
522
553
|
}
|
|
523
554
|
})
|
|
524
555
|
ws.on('close', () => {
|