dsh-over-ssh 0.1.0 → 0.1.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 +10 -10
- package/agents/agent-manifest.json +1 -1
- package/lib/client.js +363 -314
- package/lib/file-references.js +80 -35
- package/lib/fs.js +158 -75
- package/lib/index.js +4103 -3397
- package/lib/terminal-wrapper.js +9 -4
- package/lib/types/src/config.d.ts +80 -9
- package/lib/types/src/file-references.d.ts +0 -1
- package/lib/types/src/host/app/backend.d.ts +9 -1
- package/lib/types/src/host/app/recycle-bin/recycle-bin-contract.d.ts +0 -1
- package/lib/types/src/host/app/recycle-bin/recycle-bin-service.d.ts +0 -1
- package/lib/types/src/host/app/session-admin/session-admin-contract.d.ts +12 -1
- package/lib/types/src/host/app/session-admin/session-admin-service.d.ts +0 -1
- package/lib/types/src/host/app/transport/transport-selector.d.ts +0 -1
- package/lib/types/src/host/app/transport/transport-service.d.ts +0 -1
- package/lib/types/src/host/domain/capability.d.ts +0 -1
- package/lib/types/src/host/domain/errors.d.ts +0 -1
- package/lib/types/src/host/domain/roots/connection.d.ts +0 -1
- package/lib/types/src/host/domain/roots/root.d.ts +24 -9
- package/lib/types/src/host/domain/roots/session-roots.d.ts +0 -1
- package/lib/types/src/host/domain/roots/ssh-config.d.ts +0 -1
- package/lib/types/src/host/domain/session/session-admin.d.ts +0 -1
- package/lib/types/src/host/domain/transfer/recycle-bin.d.ts +0 -1
- package/lib/types/src/host/domain/transfer/transfer.d.ts +0 -1
- package/lib/types/src/host/infra/transport/agent/agent-installer.d.ts +0 -1
- package/lib/types/src/host/infra/transport/ssh/ssh2-transport.d.ts +0 -1
- package/lib/types/src/host/infra/transport/transport-base.d.ts +0 -1
- package/lib/types/src/host/infra/transport/transport.d.ts +16 -2
- package/lib/types/src/host/integration/agent/agent-preflight.d.ts +0 -1
- package/lib/types/src/host/integration/fs/file-reference-provider.d.ts +0 -1
- package/lib/types/src/host/integration/fs/fs-provider.d.ts +0 -2
- package/lib/types/src/host/integration/transfer/transfer-service.d.ts +0 -1
- package/lib/types/src/index.d.ts +23 -1
- package/lib/types/src/local-bridge-refs.d.ts +0 -1
- package/lib/types/src/local-bridge.d.ts +0 -1
- package/lib/types/src/shared/root-keys.d.ts +120 -0
- package/lib/types/src/wiring/helpers.d.ts +0 -1
- package/package.json +5 -3
- package/lib/types/src/client/controls.d.ts +0 -131
- package/lib/types/src/client/core/card-focus-trap.d.ts +0 -45
- package/lib/types/src/client/core/controls.d.ts +0 -164
- package/lib/types/src/client/core/latest.d.ts +0 -48
- package/lib/types/src/client/core/locale.d.ts +0 -38
- package/lib/types/src/client/core/message.d.ts +0 -37
- package/lib/types/src/client/core/panel-model.d.ts +0 -134
- package/lib/types/src/client/core/portal.d.ts +0 -266
- package/lib/types/src/client/core/remote.d.ts +0 -84
- package/lib/types/src/client/core/styles.d.ts +0 -458
- package/lib/types/src/client/file-tree-io.d.ts +0 -19
- package/lib/types/src/client/file-tree.d.ts +0 -271
- package/lib/types/src/client/flows/directory-browser.d.ts +0 -587
- package/lib/types/src/client/flows/session-directory.d.ts +0 -415
- package/lib/types/src/client/flows/workspace-directory-flow.d.ts +0 -480
- package/lib/types/src/client/locale.d.ts +0 -31
- package/lib/types/src/client/panel-model.d.ts +0 -37
- package/lib/types/src/client/panel.d.ts +0 -77
- package/lib/types/src/client/roots/connection-test.d.ts +0 -69
- package/lib/types/src/client/roots/roots-panel-state.d.ts +0 -307
- package/lib/types/src/client/roots/roots-panel-view.d.ts +0 -280
- package/lib/types/src/client/roots/roots-panel.d.ts +0 -364
- package/lib/types/src/client/roots/transfer-panel-view.d.ts +0 -35
- package/lib/types/src/client/roots/transfer-panel.d.ts +0 -241
- package/lib/types/src/client/roots/transfer-progress.d.ts +0 -67
- package/lib/types/src/client/roots-panel-view.d.ts +0 -236
- package/lib/types/src/client/roots-panel.d.ts +0 -347
- package/lib/types/src/client/session/session-panel-view.d.ts +0 -336
- package/lib/types/src/client/session/session-panel.d.ts +0 -322
- package/lib/types/src/client/session/session-remote-mark.d.ts +0 -110
- package/lib/types/src/client/session/session-row-actions.d.ts +0 -398
- package/lib/types/src/client/session-panel.d.ts +0 -152
- package/lib/types/src/client/styles.d.ts +0 -252
- package/lib/types/src/client/workspace-directory-flow.d.ts +0 -342
- package/lib/types/src/fs.d.ts +0 -46
- package/lib/types/src/host/app/recycle-bin/recycle-bin-move.d.ts +0 -18
- package/lib/types/src/host/app/recycle-bin/recycle-bin-world.d.ts +0 -38
- package/lib/types/src/host/app/recycle-bin-contract.d.ts +0 -215
- package/lib/types/src/host/app/recycle-bin-move.d.ts +0 -18
- package/lib/types/src/host/app/recycle-bin-service.d.ts +0 -45
- package/lib/types/src/host/app/recycle-bin-world.d.ts +0 -35
- package/lib/types/src/host/app/roots/roots-api-connections.d.ts +0 -49
- package/lib/types/src/host/app/roots/roots-api-contract.d.ts +0 -369
- package/lib/types/src/host/app/roots/roots-api-directories.d.ts +0 -13
- package/lib/types/src/host/app/roots/roots-api-http.d.ts +0 -77
- package/lib/types/src/host/app/roots/roots-api-long-poll.d.ts +0 -130
- package/lib/types/src/host/app/roots/roots-api-projections.d.ts +0 -102
- package/lib/types/src/host/app/roots/roots-api-roots.d.ts +0 -13
- package/lib/types/src/host/app/roots/roots-api-state.d.ts +0 -22
- package/lib/types/src/host/app/roots/roots-api-transfers.d.ts +0 -26
- package/lib/types/src/host/app/roots/roots-api.d.ts +0 -55
- package/lib/types/src/host/app/roots/user-roots.d.ts +0 -552
- package/lib/types/src/host/app/roots-api-connections.d.ts +0 -49
- package/lib/types/src/host/app/roots-api-contract.d.ts +0 -339
- package/lib/types/src/host/app/roots-api-directories.d.ts +0 -13
- package/lib/types/src/host/app/roots-api-http.d.ts +0 -77
- package/lib/types/src/host/app/roots-api-projections.d.ts +0 -102
- package/lib/types/src/host/app/roots-api-roots.d.ts +0 -13
- package/lib/types/src/host/app/roots-api-state.d.ts +0 -22
- package/lib/types/src/host/app/roots-api-support.d.ts +0 -182
- package/lib/types/src/host/app/roots-api-transfers.d.ts +0 -13
- package/lib/types/src/host/app/roots-api.d.ts +0 -55
- package/lib/types/src/host/app/routed-fs.d.ts +0 -239
- package/lib/types/src/host/app/serialize.d.ts +0 -37
- package/lib/types/src/host/app/session-admin/session-admin-context.d.ts +0 -36
- package/lib/types/src/host/app/session-admin/session-admin-delete.d.ts +0 -34
- package/lib/types/src/host/app/session-admin-context.d.ts +0 -36
- package/lib/types/src/host/app/session-admin-contract.d.ts +0 -214
- package/lib/types/src/host/app/session-admin-delete.d.ts +0 -34
- package/lib/types/src/host/app/session-admin-service.d.ts +0 -38
- package/lib/types/src/host/app/takeover/takeover-plan.d.ts +0 -227
- package/lib/types/src/host/app/takeover/takeover-rows.d.ts +0 -55
- package/lib/types/src/host/app/transport-selector.d.ts +0 -98
- package/lib/types/src/host/app/transport-service.d.ts +0 -75
- package/lib/types/src/host/app/user-roots.d.ts +0 -461
- package/lib/types/src/host/domain/bin-location.d.ts +0 -79
- package/lib/types/src/host/domain/connection.d.ts +0 -95
- package/lib/types/src/host/domain/glob-pattern.d.ts +0 -62
- package/lib/types/src/host/domain/literal-text.d.ts +0 -63
- package/lib/types/src/host/domain/path-grammar.d.ts +0 -84
- package/lib/types/src/host/domain/recycle-bin.d.ts +0 -337
- package/lib/types/src/host/domain/root.d.ts +0 -74
- package/lib/types/src/host/domain/roots/bin-location.d.ts +0 -91
- package/lib/types/src/host/domain/roots/route-table.d.ts +0 -102
- package/lib/types/src/host/domain/route-table.d.ts +0 -80
- package/lib/types/src/host/domain/session/terminal-target.d.ts +0 -136
- package/lib/types/src/host/domain/session-admin.d.ts +0 -330
- package/lib/types/src/host/domain/shell.d.ts +0 -47
- package/lib/types/src/host/domain/ssh-config.d.ts +0 -73
- package/lib/types/src/host/domain/terminal-target.d.ts +0 -132
- package/lib/types/src/host/domain/tool-requests.d.ts +0 -369
- package/lib/types/src/host/domain/transfer.d.ts +0 -260
- package/lib/types/src/host/infra/backend/local-backend.d.ts +0 -84
- package/lib/types/src/host/infra/backend/local-version.d.ts +0 -43
- package/lib/types/src/host/infra/backend/platform-local-backend.d.ts +0 -89
- package/lib/types/src/host/infra/backend/remote-backend.d.ts +0 -239
- package/lib/types/src/host/infra/cache/lru.d.ts +0 -52
- package/lib/types/src/host/infra/cache/versioned.d.ts +0 -77
- package/lib/types/src/host/infra/temp-name.d.ts +0 -19
- package/lib/types/src/host/infra/transport/agent/agent-protocol-client.d.ts +0 -132
- package/lib/types/src/host/infra/transport/agent/agent-protocol-codec.d.ts +0 -140
- package/lib/types/src/host/infra/transport/agent/agent-protocol-errors.d.ts +0 -41
- package/lib/types/src/host/infra/transport/agent/agent-protocol-frames.d.ts +0 -197
- package/lib/types/src/host/infra/transport/agent/agent-protocol-types.d.ts +0 -564
- package/lib/types/src/host/infra/transport/agent/agent-transport.d.ts +0 -418
- package/lib/types/src/host/infra/transport/agent-installer.d.ts +0 -121
- package/lib/types/src/host/infra/transport/agent-protocol-client.d.ts +0 -123
- package/lib/types/src/host/infra/transport/agent-protocol-codec.d.ts +0 -131
- package/lib/types/src/host/infra/transport/agent-protocol-errors.d.ts +0 -41
- package/lib/types/src/host/infra/transport/agent-protocol-frames.d.ts +0 -197
- package/lib/types/src/host/infra/transport/agent-protocol-types.d.ts +0 -509
- package/lib/types/src/host/infra/transport/agent-transport.d.ts +0 -353
- package/lib/types/src/host/infra/transport/alias-transport.d.ts +0 -206
- package/lib/types/src/host/infra/transport/askpass-fs.d.ts +0 -20
- package/lib/types/src/host/infra/transport/askpass.d.ts +0 -133
- package/lib/types/src/host/infra/transport/command-pool.d.ts +0 -61
- package/lib/types/src/host/infra/transport/fs-commands.d.ts +0 -140
- package/lib/types/src/host/infra/transport/git-status-route.d.ts +0 -63
- package/lib/types/src/host/infra/transport/git-status.d.ts +0 -101
- package/lib/types/src/host/infra/transport/node-spawn.d.ts +0 -26
- package/lib/types/src/host/infra/transport/session-window.d.ts +0 -51
- package/lib/types/src/host/infra/transport/shell-quote.d.ts +0 -59
- package/lib/types/src/host/infra/transport/ssh/alias-transport.d.ts +0 -206
- package/lib/types/src/host/infra/transport/ssh/askpass-fs.d.ts +0 -20
- package/lib/types/src/host/infra/transport/ssh/askpass.d.ts +0 -133
- package/lib/types/src/host/infra/transport/ssh/shell-quote.d.ts +0 -62
- package/lib/types/src/host/infra/transport/ssh/ssh-argv.d.ts +0 -43
- package/lib/types/src/host/infra/transport/ssh-argv.d.ts +0 -43
- package/lib/types/src/host/infra/transport/ssh2-transport.d.ts +0 -302
- package/lib/types/src/host/integration/agent/agent-install-io.d.ts +0 -33
- package/lib/types/src/host/integration/agent/askpass-registry.d.ts +0 -66
- package/lib/types/src/host/integration/agent-install-io.d.ts +0 -33
- package/lib/types/src/host/integration/agent-preflight.d.ts +0 -56
- package/lib/types/src/host/integration/anchor-directory.d.ts +0 -56
- package/lib/types/src/host/integration/askpass-registry.d.ts +0 -59
- package/lib/types/src/host/integration/bin-root-resolver.d.ts +0 -81
- package/lib/types/src/host/integration/connection-test.d.ts +0 -73
- package/lib/types/src/host/integration/file-reference-provider.d.ts +0 -115
- package/lib/types/src/host/integration/fs/git-status-wiring.d.ts +0 -21
- package/lib/types/src/host/integration/fs/platform-errors.d.ts +0 -94
- package/lib/types/src/host/integration/fs/remote-file-tools.d.ts +0 -369
- package/lib/types/src/host/integration/fs/remote-index.d.ts +0 -177
- package/lib/types/src/host/integration/fs-provider.d.ts +0 -226
- package/lib/types/src/host/integration/lsp/lsp-framing.d.ts +0 -38
- package/lib/types/src/host/integration/lsp/lsp-registry.d.ts +0 -67
- package/lib/types/src/host/integration/lsp/lsp-session.d.ts +0 -130
- package/lib/types/src/host/integration/management-tools.d.ts +0 -64
- package/lib/types/src/host/integration/recycle-bin-service.d.ts +0 -247
- package/lib/types/src/host/integration/remote/connection-test.d.ts +0 -73
- package/lib/types/src/host/integration/remote/remote-face.d.ts +0 -127
- package/lib/types/src/host/integration/remote/remote-tool-backend.d.ts +0 -108
- package/lib/types/src/host/integration/remote-face.d.ts +0 -127
- package/lib/types/src/host/integration/remote-file-tools.d.ts +0 -241
- package/lib/types/src/host/integration/remote-home.d.ts +0 -69
- package/lib/types/src/host/integration/remote-index.d.ts +0 -165
- package/lib/types/src/host/integration/remote-job-tools.d.ts +0 -295
- package/lib/types/src/host/integration/remote-tool-backend.d.ts +0 -97
- package/lib/types/src/host/integration/remote-tools.d.ts +0 -206
- package/lib/types/src/host/integration/root-registry.d.ts +0 -115
- package/lib/types/src/host/integration/roots/anchor-directory.d.ts +0 -56
- package/lib/types/src/host/integration/roots/bin-root-resolver.d.ts +0 -81
- package/lib/types/src/host/integration/roots/remote-home.d.ts +0 -69
- package/lib/types/src/host/integration/roots/root-registry.d.ts +0 -144
- package/lib/types/src/host/integration/roots/roots-wiring.d.ts +0 -219
- package/lib/types/src/host/integration/roots/session-root-index.d.ts +0 -52
- package/lib/types/src/host/integration/roots-api.d.ts +0 -407
- package/lib/types/src/host/integration/roots-wiring.d.ts +0 -211
- package/lib/types/src/host/integration/session/session-api.d.ts +0 -88
- package/lib/types/src/host/integration/session/session-bin-lock.d.ts +0 -80
- package/lib/types/src/host/integration/session/session-header.d.ts +0 -151
- package/lib/types/src/host/integration/session/session-labels.d.ts +0 -56
- package/lib/types/src/host/integration/session/session-route-wiring.d.ts +0 -50
- package/lib/types/src/host/integration/session-admin-service.d.ts +0 -236
- package/lib/types/src/host/integration/session-api.d.ts +0 -81
- package/lib/types/src/host/integration/session-bin-lock.d.ts +0 -80
- package/lib/types/src/host/integration/session-header.d.ts +0 -118
- package/lib/types/src/host/integration/session-labels.d.ts +0 -56
- package/lib/types/src/host/integration/session-route-wiring.d.ts +0 -50
- package/lib/types/src/host/integration/session-scope.d.ts +0 -113
- package/lib/types/src/host/integration/session-tools.d.ts +0 -44
- package/lib/types/src/host/integration/strict-arguments.d.ts +0 -79
- package/lib/types/src/host/integration/takeover/takeover-api.d.ts +0 -62
- package/lib/types/src/host/integration/takeover/takeover-route-wiring.d.ts +0 -60
- package/lib/types/src/host/integration/takeover/takeover-service.d.ts +0 -167
- package/lib/types/src/host/integration/terminal-manifest.d.ts +0 -94
- package/lib/types/src/host/integration/tools/management-tools.d.ts +0 -245
- package/lib/types/src/host/integration/tools/remote-job-tools.d.ts +0 -316
- package/lib/types/src/host/integration/tools/remote-lsp-tools.d.ts +0 -43
- package/lib/types/src/host/integration/tools/remote-tools.d.ts +0 -236
- package/lib/types/src/host/integration/tools/session-target.d.ts +0 -52
- package/lib/types/src/host/integration/tools/session-tools.d.ts +0 -46
- package/lib/types/src/host/integration/tools/strict-arguments.d.ts +0 -79
- package/lib/types/src/host/integration/tools/terminal-manifest.d.ts +0 -94
- package/lib/types/src/host/integration/transfer/remote-copy.d.ts +0 -66
- package/lib/types/src/host/integration/transfer/transfer-adapters.d.ts +0 -48
- package/lib/types/src/host/integration/transfer/transfer-tracker.d.ts +0 -106
- package/lib/types/src/host/integration/transfer-adapters.d.ts +0 -44
- package/lib/types/src/host/integration/transfer-service.d.ts +0 -164
- package/lib/types/src/host/integration/transfer-tracker.d.ts +0 -106
- package/lib/types/src/host/integration/user-roots.d.ts +0 -461
- package/lib/types/src/wiring/connections.d.ts +0 -189
- package/lib/types/src/wiring/fs.d.ts +0 -13
- package/lib/types/src/wiring/install-memo.d.ts +0 -32
- package/lib/types/src/wiring/legacy-dirs.d.ts +0 -62
- package/lib/types/src/wiring/package-paths.d.ts +0 -35
- package/lib/types/src/wiring/roots.d.ts +0 -21
- package/lib/types/src/wiring/state-dir.d.ts +0 -29
- package/lib/types/src/wiring/tools.d.ts +0 -35
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The `ssh2` transport: a pure-JS SSH client.
|
|
3
|
-
*
|
|
4
|
-
* Why a second implementation:
|
|
5
|
-
*
|
|
6
|
-
* - **Password authentication** cannot be done by a non-interactive system `ssh` (it hardcodes
|
|
7
|
-
* `BatchMode`-style behaviour and would block on a prompt).
|
|
8
|
-
* - **Dynamic ProxyJump** chains (per-connection host/port/credentials) cannot be expressed
|
|
9
|
-
* through the system client's argv without writing a temporary ssh config.
|
|
10
|
-
* - **Connection reuse on Windows**: the host platform's OpenSSH does not support ControlMaster
|
|
11
|
-
* (measured: `getsockname failed: Not a socket`), so the only way to reuse one connection for
|
|
12
|
-
* many commands there is a client we own. See `doc/03-decisions/0006-windows-host-no-controlmaster.md`.
|
|
13
|
-
*
|
|
14
|
-
* Everything the plugin needs from the remote host is expressed with the *same* helpers as the
|
|
15
|
-
* alias transport — the same probe script, the same shell quoting, the same `size:mtimeSeconds`
|
|
16
|
-
* version token, the same failure classification — so behaviour does not change when a connection
|
|
17
|
-
* switches transports. File bytes travel over the SFTP subsystem (part of OpenSSH, still zero
|
|
18
|
-
* install) instead of base64 over the exec channel.
|
|
19
|
-
*
|
|
20
|
-
* The client itself is injected (`Ssh2Connector`), so this module never imports the `ssh2`
|
|
21
|
-
* package: unit tests run without it and without a network.
|
|
22
|
-
*
|
|
23
|
-
* @module dsh-remote-workspace/infra/transport/ssh2-transport
|
|
24
|
-
*/
|
|
25
|
-
import { type RemoteCapabilities } from '../../domain/capability.ts';
|
|
26
|
-
import type { HostKeyPolicy } from '../../domain/connection.ts';
|
|
27
|
-
import { SshDevError } from '../../domain/errors.ts';
|
|
28
|
-
import { type ExecRequest, type ExecResult, type ReadFileRangeRequest, type ReadFileRequest, type RemoteFileChunk, type RemoteWriteOutcome, type RemoveRequest, type RenameRequest, type Transport, type WriteFileRangeRequest, type WriteFileRequest } from './transport.ts';
|
|
29
|
-
import { TransportBase } from './transport-base.ts';
|
|
30
|
-
/** Authentication material for one hop. */
|
|
31
|
-
export interface Ssh2Auth {
|
|
32
|
-
readonly password?: string;
|
|
33
|
-
readonly privateKey?: string | Uint8Array;
|
|
34
|
-
readonly passphrase?: string;
|
|
35
|
-
/** ssh-agent socket path, or `pageant` on Windows. */
|
|
36
|
-
readonly agent?: string;
|
|
37
|
-
}
|
|
38
|
-
/** One host to connect to. */
|
|
39
|
-
export interface Ssh2Target {
|
|
40
|
-
readonly host: string;
|
|
41
|
-
readonly port?: number;
|
|
42
|
-
readonly username: string;
|
|
43
|
-
readonly auth: Ssh2Auth;
|
|
44
|
-
}
|
|
45
|
-
/** Everything needed to open a session. */
|
|
46
|
-
export interface Ssh2ConnectConfig {
|
|
47
|
-
/** Final target. */
|
|
48
|
-
readonly target: Ssh2Target;
|
|
49
|
-
/** Ordered jump hosts: hop 0 is reached directly, each next hop through the previous one. */
|
|
50
|
-
readonly jump?: readonly Ssh2Target[];
|
|
51
|
-
readonly hostKeyPolicy?: HostKeyPolicy;
|
|
52
|
-
/** Accepted host key fingerprints (`SHA256:…`), required when the policy is `strict`. */
|
|
53
|
-
readonly trustedHostKeys?: readonly string[];
|
|
54
|
-
readonly connectTimeoutMs?: number;
|
|
55
|
-
readonly keepAliveMs?: number;
|
|
56
|
-
/** Cancellation for the connect attempt. */
|
|
57
|
-
readonly signal?: AbortSignal;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* An SFTP file handle.
|
|
61
|
-
*
|
|
62
|
-
* Opaque on purpose: `ssh2` hands back a raw `Buffer` handle that is only meaningful to
|
|
63
|
-
* `sftp.read` / `sftp.close` (verified against the real library — there is **no**
|
|
64
|
-
* `handle.read` method, which is exactly the mistake this shape prevents).
|
|
65
|
-
*/
|
|
66
|
-
export type Ssh2FileHandle = unknown;
|
|
67
|
-
/** SFTP statistics as far as this transport needs them. */
|
|
68
|
-
export interface Ssh2Stats {
|
|
69
|
-
/** Base name of a `readdir` entry; absent on objects returned by `stat`. */
|
|
70
|
-
readonly filename?: string;
|
|
71
|
-
readonly size: number;
|
|
72
|
-
/**
|
|
73
|
-
* Modification time in **seconds** since the epoch.
|
|
74
|
-
*
|
|
75
|
-
* SFTP v3 attributes carry seconds, and `ssh2` passes them through unchanged (verified on a
|
|
76
|
-
* real host: `mtime = 1789217901`). The alias transport's `stat -c %Y` also reports seconds, so
|
|
77
|
-
* keeping the same unit is what makes version tokens comparable across transports.
|
|
78
|
-
*/
|
|
79
|
-
readonly mtime: number;
|
|
80
|
-
/**
|
|
81
|
-
* SFTP file mode bits (`stats.mode & 0o170000`).
|
|
82
|
-
*
|
|
83
|
-
* Optional because a caller may hand over a `Stats`-shaped object without it, but the real
|
|
84
|
-
* library always sets it. It is the only reliable way to detect a **symbolic link** during a
|
|
85
|
-
* recursive removal: SFTP v3 `readdir` reports the link itself and never follows it.
|
|
86
|
-
*/
|
|
87
|
-
readonly mode?: number;
|
|
88
|
-
/** `readdir` long name; starts with `l` for a symbolic link (`-` file, `d` directory). */
|
|
89
|
-
readonly longname?: string;
|
|
90
|
-
isDirectory?(): boolean;
|
|
91
|
-
isFile?(): boolean;
|
|
92
|
-
isSymbolicLink?(): boolean;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* The narrow SFTP surface this transport uses, mirroring the real `ssh2` API:
|
|
96
|
-
* `open` yields a handle, and reads go through `read(handle, …)`, not through the handle.
|
|
97
|
-
*
|
|
98
|
-
* `readdir` / `rmdir` are optional for the same reason `ext_openssh_rename` is: a fake or a
|
|
99
|
-
* non-OpenSSH server may not provide them, and {@link Ssh2Transport.remove} must then report
|
|
100
|
-
* `REMOTE_UNSUPPORTED` instead of pretending a tree went away.
|
|
101
|
-
*/
|
|
102
|
-
export interface Ssh2Sftp {
|
|
103
|
-
stat(path: string, callback: (error?: Error, stats?: Ssh2Stats) => void): void;
|
|
104
|
-
open(path: string, flags: string, callback: (error?: Error, handle?: Ssh2FileHandle) => void): void;
|
|
105
|
-
/** Read into a real `Buffer` (the library rejects plain `Uint8Array` views). */
|
|
106
|
-
read(handle: Ssh2FileHandle, buffer: Buffer, offset: number, length: number, position: number, callback: (error?: Error, bytesRead?: number) => void): void;
|
|
107
|
-
/**
|
|
108
|
-
* Positioned write into an open handle. `position` is the **file** offset; `offset` is the
|
|
109
|
-
* offset inside `buffer` (the same parameter order as the real library).
|
|
110
|
-
*/
|
|
111
|
-
write(handle: Ssh2FileHandle, buffer: Buffer, offset: number, length: number, position: number, callback: (error?: Error) => void): void;
|
|
112
|
-
close(handle: Ssh2FileHandle, callback: (error?: Error) => void): void;
|
|
113
|
-
/** Statistics for an **open handle**, used to size a positioned write before truncating. */
|
|
114
|
-
fstat?(handle: Ssh2FileHandle, callback: (error?: Error, stats?: Ssh2Stats) => void): void;
|
|
115
|
-
/** Truncate an open handle to `length` bytes. */
|
|
116
|
-
ftruncate?(handle: Ssh2FileHandle, length: number, callback: (error?: Error) => void): void;
|
|
117
|
-
writeFile(path: string, data: Uint8Array, callback: (error?: Error) => void): void;
|
|
118
|
-
mkdir(path: string, attributes: {
|
|
119
|
-
readonly mode: number;
|
|
120
|
-
}, callback: (error?: Error) => void): void;
|
|
121
|
-
/** List a directory. The v3 protocol returns `.` and `..` too, so callers must skip them. */
|
|
122
|
-
readdir?(path: string, callback: (error?: Error, entries?: readonly Ssh2Stats[]) => void): void;
|
|
123
|
-
/** Remove an **empty** directory. */
|
|
124
|
-
rmdir?(path: string, callback: (error?: Error) => void): void;
|
|
125
|
-
/**
|
|
126
|
-
* Plain SFTP rename.
|
|
127
|
-
*
|
|
128
|
-
* Measured on a real OpenSSH server: this **fails with SSH_FX_FAILURE when the destination
|
|
129
|
-
* already exists**, so it cannot be the only path for an overwriting write.
|
|
130
|
-
*/
|
|
131
|
-
rename(from: string, to: string, callback: (error?: Error) => void): void;
|
|
132
|
-
/**
|
|
133
|
-
* `posix-rename@openssh.com`: atomic overwrite. Present on OpenSSH servers/clients, absent
|
|
134
|
-
* elsewhere — which is why {@link writeFile} keeps a fallback ladder.
|
|
135
|
-
*/
|
|
136
|
-
ext_openssh_rename?: ((from: string, to: string, callback: (error?: Error) => void) => void) | undefined;
|
|
137
|
-
unlink(path: string, callback: (error?: Error) => void): void;
|
|
138
|
-
end(): void;
|
|
139
|
-
}
|
|
140
|
-
/** A command channel as far as this transport needs it. */
|
|
141
|
-
export interface Ssh2Channel {
|
|
142
|
-
on(event: 'data', listener: (chunk: Buffer) => void): this;
|
|
143
|
-
on(event: 'close', listener: (code: number | null, signal: string | null) => void): this;
|
|
144
|
-
on(event: string, listener: (...args: never[]) => void): this;
|
|
145
|
-
stderr: {
|
|
146
|
-
on(event: 'data', listener: (chunk: Buffer) => void): unknown;
|
|
147
|
-
};
|
|
148
|
-
write(data: string | Uint8Array): boolean;
|
|
149
|
-
end(data?: string | Uint8Array): void;
|
|
150
|
-
close(): void;
|
|
151
|
-
}
|
|
152
|
-
/** An open SSH session: many channels, one connection. */
|
|
153
|
-
export interface Ssh2Session {
|
|
154
|
-
exec(command: string, options: {
|
|
155
|
-
readonly pty?: boolean;
|
|
156
|
-
}, callback: (error?: Error, channel?: Ssh2Channel) => void): void;
|
|
157
|
-
sftp(callback: (error?: Error, sftp?: Ssh2Sftp) => void): void;
|
|
158
|
-
/** Open a TCP channel through this session, used to chain jump hosts. */
|
|
159
|
-
forwardOut(sourceAddress: string, sourcePort: number, destinationAddress: string, destinationPort: number, callback: (error?: Error, stream?: unknown) => void): void;
|
|
160
|
-
end(): void;
|
|
161
|
-
destroy(): void;
|
|
162
|
-
/**
|
|
163
|
-
* Intermediate jump-host sessions this session rides on, so `dispose()` can close them too.
|
|
164
|
-
*
|
|
165
|
-
* **Deliberately per-session, not a module-level slot.** The first version of this fix kept them in
|
|
166
|
-
* a module-level `lastHops`, which is wrong as soon as one process has two connections
|
|
167
|
-
* (`TransportService` caches by `profile.id`, so multiple profiles are normal):
|
|
168
|
-
* - the second `connect` cleared the array, so the first connection's hops were **leaked**;
|
|
169
|
-
* - the first `dispose()` then took the *second* connection's hops and **ended a live connection
|
|
170
|
-
* another transport was still using**.
|
|
171
|
-
* Hanging them on the session they belong to makes ownership structural instead of positional.
|
|
172
|
-
* `ssh2`'s real `Client` has no such property, so it is optional and set only by
|
|
173
|
-
* {@link defaultConnector}.
|
|
174
|
-
*/
|
|
175
|
-
readonly hops?: readonly Ssh2Session[];
|
|
176
|
-
}
|
|
177
|
-
/** Opens a session; injected so tests never load the real client. */
|
|
178
|
-
export type Ssh2Connector = (config: Ssh2ConnectConfig) => Promise<Ssh2Session>;
|
|
179
|
-
/** Construction options. */
|
|
180
|
-
export interface Ssh2TransportOptions {
|
|
181
|
-
readonly connectionId: string;
|
|
182
|
-
/** Session factory. Defaults to the real `ssh2` client (loaded lazily). */
|
|
183
|
-
readonly connector?: Ssh2Connector;
|
|
184
|
-
readonly connect?: Ssh2ConnectConfig;
|
|
185
|
-
readonly commandTimeoutMs?: number;
|
|
186
|
-
readonly now?: () => number;
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Bytes of a command's **stderr** that are retained.
|
|
190
|
-
*
|
|
191
|
-
* `maxBytes` bounds stdout only, and stderr used to be accumulated without any bound at all: a command
|
|
192
|
-
* that writes diagnostics in a loop (`ssh -v`, a verbose build, a remote program that logs errors) grew
|
|
193
|
-
* this process's heap until the channel closed. Measured before the fix: a peer emitting 64 MiB of
|
|
194
|
-
* stderr retained **all 64 MiB** while stdout was correctly capped at 1 KiB (审计 0050).
|
|
195
|
-
*
|
|
196
|
-
* The **tail** is kept, not the head: every consumer of this string wants the last thing the command
|
|
197
|
-
* said. `domain/errors.ts` reduces it with `tail(stderr, 2000)` for classification, and a failing
|
|
198
|
-
* command's useful diagnostic (the final error, the `DSH_ERROR=` refusal) is at the end.
|
|
199
|
-
*/
|
|
200
|
-
export declare const SSH2_STDERR_MAX_BYTES: number;
|
|
201
|
-
/**
|
|
202
|
-
* First line prepended to a truncated stderr capture.
|
|
203
|
-
*
|
|
204
|
-
* The truncation is announced **in the string itself** because `ExecResult.truncated` is stdout's flag
|
|
205
|
-
* (see its JSDoc) and must keep that meaning: `remote-backend` treats a truncated batch read as "these
|
|
206
|
-
* paths were not read", so setting it for a chatty command's stderr would report readable files as
|
|
207
|
-
* missing. Announcing it here keeps the diagnostic honest without overloading the flag.
|
|
208
|
-
*/
|
|
209
|
-
export declare const STDERR_TRUNCATION_NOTICE = "\uFF08stderr \u8FC7\u957F\uFF0C\u4EC5\u4FDD\u7559\u672B\u5C3E\u7247\u6BB5\uFF09";
|
|
210
|
-
/**
|
|
211
|
-
* The pure-JS transport.
|
|
212
|
-
*
|
|
213
|
-
* One session serves every command and every file transfer, which is what gives Windows hosts
|
|
214
|
-
* the connection reuse their OpenSSH client cannot provide.
|
|
215
|
-
*/
|
|
216
|
-
export declare class Ssh2Transport extends TransportBase implements Transport {
|
|
217
|
-
#private;
|
|
218
|
-
readonly id: string;
|
|
219
|
-
constructor(options: Ssh2TransportOptions);
|
|
220
|
-
get sessionCount(): number;
|
|
221
|
-
/** Open the session; concurrent calls share one attempt. */
|
|
222
|
-
connect(signal?: AbortSignal): Promise<void>;
|
|
223
|
-
/** Close the session. Idempotent. */
|
|
224
|
-
dispose(): Promise<void>;
|
|
225
|
-
/** Run one remote command over a channel of the shared session. */
|
|
226
|
-
exec(request: ExecRequest, signal?: AbortSignal): Promise<ExecResult>;
|
|
227
|
-
/**
|
|
228
|
-
* Read a remote file over SFTP.
|
|
229
|
-
*
|
|
230
|
-
* The version token is `size:mtimeSeconds` — the same shape the alias transport produces, so
|
|
231
|
-
* caches keyed on version tokens keep working when a connection changes transport.
|
|
232
|
-
*/
|
|
233
|
-
readFile(request: ReadFileRequest, signal?: AbortSignal): Promise<RemoteFileChunk>;
|
|
234
|
-
/**
|
|
235
|
-
* Read `maxBytes` from `offset` over SFTP, seeking with a real `position`.
|
|
236
|
-
*
|
|
237
|
-
* Why the seek matters: `readFile` always starts at 0, so paging through a large file by
|
|
238
|
-
* reading `[0, offset+len)` and slicing transfers every earlier byte on every page. Here the
|
|
239
|
-
* server only sends the requested window, and only `maxBytes` is ever allocated locally.
|
|
240
|
-
*/
|
|
241
|
-
readFileRange(request: ReadFileRangeRequest, signal?: AbortSignal): Promise<RemoteFileChunk>;
|
|
242
|
-
/**
|
|
243
|
-
* Write at `offset` over SFTP, leaving the bytes before it untouched.
|
|
244
|
-
*
|
|
245
|
-
* Flags are chosen by offset, and the semantics are deliberately narrow:
|
|
246
|
-
*
|
|
247
|
-
* - `offset === 0` uses `'w'`: the file is **created or truncated**, so a chunked upload that
|
|
248
|
-
* restarts from 0 overwrites a longer previous file instead of leaving a stale tail.
|
|
249
|
-
* - `offset > 0` uses `'r+'`: the file **must already exist** (there is no other way to preserve
|
|
250
|
-
* the earlier bytes), otherwise `REMOTE_NOT_FOUND`. After the positioned write the file is
|
|
251
|
-
* truncated to `offset + bytes.byteLength` — a shorter final chunk must shorten the file, or
|
|
252
|
-
* the tail of a previous, longer upload would survive and corrupt the result.
|
|
253
|
-
*/
|
|
254
|
-
writeFileRange(request: WriteFileRangeRequest, signal?: AbortSignal): Promise<RemoteWriteOutcome>;
|
|
255
|
-
/**
|
|
256
|
-
* Move a remote path with a plain SFTP `rename`.
|
|
257
|
-
*
|
|
258
|
-
* A cross-device move makes the server fail (`SSH_FX_FAILURE` / `EXDEV`) and that failure is
|
|
259
|
-
* **propagated as-is**: copying would silently turn a move into a copy, which breaks the
|
|
260
|
-
* "move, then delete the source only after confirmation" semantics callers rely on.
|
|
261
|
-
*/
|
|
262
|
-
rename(request: RenameRequest, signal?: AbortSignal): Promise<void>;
|
|
263
|
-
/**
|
|
264
|
-
* Remove a file, or a directory tree when `recursive` is set.
|
|
265
|
-
*
|
|
266
|
-
* A directory without `recursive` is an explicit error, never a silent success: a caller that
|
|
267
|
-
* hears "removed" must be able to trust that the path is gone.
|
|
268
|
-
*
|
|
269
|
-
* Symbolic links are never followed. SFTP v3 `readdir` reports a link as an entry with
|
|
270
|
-
* `mode & 0o170000 === 0o120000`; treating it as a directory would either recurse forever on a
|
|
271
|
-
* self-referential link or descend into a tree that was never requested for deletion.
|
|
272
|
-
*/
|
|
273
|
-
remove(request: RemoveRequest, signal?: AbortSignal): Promise<void>;
|
|
274
|
-
/**
|
|
275
|
-
* Write a remote file over SFTP: temp file in the target directory, then a rename.
|
|
276
|
-
*
|
|
277
|
-
* The optional `expectedSha256` guard runs the same remote digest tool the alias transport
|
|
278
|
-
* uses, so a guarded write means the same thing on both transports.
|
|
279
|
-
*/
|
|
280
|
-
writeFile(request: WriteFileRequest, signal?: AbortSignal): Promise<RemoteWriteOutcome>;
|
|
281
|
-
/**
|
|
282
|
-
* Probe the remote host once and remember the result.
|
|
283
|
-
*
|
|
284
|
-
* @param signal - cancellation.
|
|
285
|
-
* @param maxAgeMs - reuse a previous probe inside this window (default 5 minutes).
|
|
286
|
-
* @returns the discovered capabilities.
|
|
287
|
-
*/
|
|
288
|
-
probeCapabilities(signal?: AbortSignal, maxAgeMs?: number): Promise<RemoteCapabilities>;
|
|
289
|
-
}
|
|
290
|
-
/** Version token shared by every transport: `size:mtimeSeconds`. */
|
|
291
|
-
export declare function versionToken(size: number, mtimeSeconds: number): string;
|
|
292
|
-
/** Map an SFTP failure onto a plugin error code. */
|
|
293
|
-
export declare function sftpError(operation: string, path: string, error?: Error): SshDevError;
|
|
294
|
-
/**
|
|
295
|
-
* The real connector: wraps the `ssh2` package.
|
|
296
|
-
*
|
|
297
|
-
* Loaded lazily so this module (and its unit tests) never require the dependency, and required
|
|
298
|
-
* for dynamic jump chains: each hop is reached by forwarding a TCP channel through the previous
|
|
299
|
-
* session.
|
|
300
|
-
*/
|
|
301
|
-
export declare const defaultConnector: Ssh2Connector;
|
|
302
|
-
//# sourceMappingURL=ssh2-transport.d.ts.map
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The install ladder's I/O, built on the connection's **Tier 1 transport**.
|
|
3
|
-
*
|
|
4
|
-
* The agent cannot install itself, so every step of the ladder runs through the transport that already
|
|
5
|
-
* works: `exec` for the probe, `writeFile` for the upload (the Tier 1 transports already implement bounded,
|
|
6
|
-
* atomic remote writes), and one more `exec` for the digest. Nothing here opens a second authentication
|
|
7
|
-
* path, which is what keeps "upload" as trustworthy as "run a command".
|
|
8
|
-
*
|
|
9
|
-
* @module dsh-over-ssh/integration/agent-install-io
|
|
10
|
-
*/
|
|
11
|
-
import type { AgentInstallIo } from '../../infra/transport/agent/agent-installer.ts';
|
|
12
|
-
import type { Transport } from '../../infra/transport/transport.ts';
|
|
13
|
-
/** Options for {@link createAgentInstallIo}. */
|
|
14
|
-
export interface AgentInstallIoOptions {
|
|
15
|
-
/** The Tier 1 transport of the connection being installed onto. */
|
|
16
|
-
readonly transport: Transport;
|
|
17
|
-
/** Refuse to upload anything larger than this (a corrupt manifest must not fill the host's disk). */
|
|
18
|
-
readonly maxUploadBytes?: number;
|
|
19
|
-
/** Local file reader, injected so tests never touch a real file. */
|
|
20
|
-
readonly readLocalFile?: (path: string) => Promise<Uint8Array>;
|
|
21
|
-
/** Local size lookup, so an oversized artifact is rejected before it is read into memory. */
|
|
22
|
-
readonly localSize?: (path: string) => Promise<number>;
|
|
23
|
-
/** Cap for exec output while probing; the ladder only needs tiny answers. */
|
|
24
|
-
readonly probeMaxBytes?: number;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Build the ladder's I/O from an existing transport.
|
|
28
|
-
*
|
|
29
|
-
* @param options - transport plus optional readers and caps.
|
|
30
|
-
* @returns an {@link AgentInstallIo} that never opens a second connection.
|
|
31
|
-
*/
|
|
32
|
-
export declare function createAgentInstallIo(options: AgentInstallIoOptions): AgentInstallIo;
|
|
33
|
-
//# sourceMappingURL=agent-install-io.d.ts.map
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-connection store of `SSH_ASKPASS` helpers.
|
|
3
|
-
*
|
|
4
|
-
* Why this exists as a module: preparing a helper is **async** (it reads the secret from the credential
|
|
5
|
-
* service and writes two files), but transport factories are **synchronous** and are also reached from
|
|
6
|
-
* `createAlias` during the install ladder. The two are joined here: something async calls
|
|
7
|
-
* {@link AskpassRegistry.prepare} first, and the factory then reads {@link AskpassRegistry.envFor}
|
|
8
|
-
* without awaiting.
|
|
9
|
-
*
|
|
10
|
-
* Three rules:
|
|
11
|
-
*
|
|
12
|
-
* 1. **One helper per connection.** Preparing twice returns the same handle, so a reconnect does not
|
|
13
|
-
* accumulate temp directories (and the secret is read once).
|
|
14
|
-
* 2. **Only password connections get one.** Key and agent connections never prompt; handing them
|
|
15
|
-
* `BatchMode=no` would let a misconfigured host block on an interactive prompt forever.
|
|
16
|
-
* 3. **A failure is a degradation, not a throw.** If the secret cannot be resolved the connection still
|
|
17
|
-
* works through the transports that do not need it; {@link AskpassRegistry.prepare} reports `undefined`
|
|
18
|
-
* and the caller keeps its previous behaviour.
|
|
19
|
-
*
|
|
20
|
-
* @module dsh-over-ssh/integration/askpass-registry
|
|
21
|
-
*/
|
|
22
|
-
import { type AskpassHandle } from '../../infra/transport/ssh/askpass.ts';
|
|
23
|
-
import type { ConnectionProfile } from '../../domain/roots/connection.ts';
|
|
24
|
-
/** Options for {@link createAskpassRegistry}. */
|
|
25
|
-
export interface AskpassRegistryOptions {
|
|
26
|
-
/** Resolve the connection's password. Throws when it is missing or the credential service is absent. */
|
|
27
|
-
readonly readSecret: (profile: ConnectionProfile) => Promise<string>;
|
|
28
|
-
/** Materialise one helper for a secret. */
|
|
29
|
-
readonly create: (secret: string) => Promise<AskpassHandle>;
|
|
30
|
-
/** Optional sink for "we could not prepare one" so the reason is not swallowed. */
|
|
31
|
-
readonly onUnavailable?: (connectionId: string, reason: string) => void;
|
|
32
|
-
}
|
|
33
|
-
/** The registry surface the composition root needs. */
|
|
34
|
-
export interface AskpassRegistry {
|
|
35
|
-
/**
|
|
36
|
-
* Ensure a helper exists for this connection.
|
|
37
|
-
*
|
|
38
|
-
* @param profile - the connection.
|
|
39
|
-
* @returns the environment to hand `ssh`, or `undefined` when this connection needs none or preparation
|
|
40
|
-
* failed (in which case the caller must keep its non-prompting behaviour).
|
|
41
|
-
*/
|
|
42
|
-
prepare(profile: ConnectionProfile): Promise<Record<string, string> | undefined>;
|
|
43
|
-
/** The environment prepared for this connection, if any. Synchronous, for transport factories. */
|
|
44
|
-
envFor(profile: ConnectionProfile): Record<string, string> | undefined;
|
|
45
|
-
/** Whether a connection is currently using a password helper. */
|
|
46
|
-
has(connectionId: string): boolean;
|
|
47
|
-
/** Drop one connection's helper (its transport was released, or the connection changed). */
|
|
48
|
-
forget(connectionId: string): Promise<void>;
|
|
49
|
-
/**
|
|
50
|
-
* Allow a retired connection id to prepare a helper again.
|
|
51
|
-
*
|
|
52
|
-
* Call this only when the connection is deliberately re-established. Without it, a re-saved connection
|
|
53
|
-
* could never get a password helper, because `forget` leaves a tombstone that `prepare` honours.
|
|
54
|
-
*/
|
|
55
|
-
revive(connectionId: string): void;
|
|
56
|
-
/** Drop every helper (plugin unload). */
|
|
57
|
-
disposeAll(): Promise<void>;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Build the registry.
|
|
61
|
-
*
|
|
62
|
-
* @param options - injected secret reader, helper factory and optional sink.
|
|
63
|
-
* @returns the registry.
|
|
64
|
-
*/
|
|
65
|
-
export declare function createAskpassRegistry(options: AskpassRegistryOptions): AskpassRegistry;
|
|
66
|
-
//# sourceMappingURL=askpass-registry.d.ts.map
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The install ladder's I/O, built on the connection's **Tier 1 transport**.
|
|
3
|
-
*
|
|
4
|
-
* The agent cannot install itself, so every step of the ladder runs through the transport that already
|
|
5
|
-
* works: `exec` for the probe, `writeFile` for the upload (the Tier 1 transports already implement bounded,
|
|
6
|
-
* atomic remote writes), and one more `exec` for the digest. Nothing here opens a second authentication
|
|
7
|
-
* path, which is what keeps "upload" as trustworthy as "run a command".
|
|
8
|
-
*
|
|
9
|
-
* @module dsh-remote-workspace/integration/agent-install-io
|
|
10
|
-
*/
|
|
11
|
-
import type { AgentInstallIo } from '../infra/transport/agent-installer.ts';
|
|
12
|
-
import type { Transport } from '../infra/transport/transport.ts';
|
|
13
|
-
/** Options for {@link createAgentInstallIo}. */
|
|
14
|
-
export interface AgentInstallIoOptions {
|
|
15
|
-
/** The Tier 1 transport of the connection being installed onto. */
|
|
16
|
-
readonly transport: Transport;
|
|
17
|
-
/** Refuse to upload anything larger than this (a corrupt manifest must not fill the host's disk). */
|
|
18
|
-
readonly maxUploadBytes?: number;
|
|
19
|
-
/** Local file reader, injected so tests never touch a real file. */
|
|
20
|
-
readonly readLocalFile?: (path: string) => Promise<Uint8Array>;
|
|
21
|
-
/** Local size lookup, so an oversized artifact is rejected before it is read into memory. */
|
|
22
|
-
readonly localSize?: (path: string) => Promise<number>;
|
|
23
|
-
/** Cap for exec output while probing; the ladder only needs tiny answers. */
|
|
24
|
-
readonly probeMaxBytes?: number;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Build the ladder's I/O from an existing transport.
|
|
28
|
-
*
|
|
29
|
-
* @param options - transport plus optional readers and caps.
|
|
30
|
-
* @returns an {@link AgentInstallIo} that never opens a second connection.
|
|
31
|
-
*/
|
|
32
|
-
export declare function createAgentInstallIo(options: AgentInstallIoOptions): AgentInstallIo;
|
|
33
|
-
//# sourceMappingURL=agent-install-io.d.ts.map
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The bridge between the install ladder and transport selection.
|
|
3
|
-
*
|
|
4
|
-
* Selection is synchronous (it may only consult cached facts) while the ladder must talk to the host, so
|
|
5
|
-
* the two are joined by an **async preflight**: the selector's first answer is revised before any transport
|
|
6
|
-
* is built. That is what makes "agent unavailable ⇒ Tier 1" true without making the selector depend on I/O.
|
|
7
|
-
*
|
|
8
|
-
* Two rules this module exists to enforce:
|
|
9
|
-
*
|
|
10
|
-
* 1. **No host traffic unless the connection asked for the agent.** A non-agent decision returns
|
|
11
|
-
* immediately, so an ordinary alias connection never probes or uploads anything.
|
|
12
|
-
* 2. **A failed ladder is a degradation, never an exception.** `ensureAgent` already turns every remote
|
|
13
|
-
* problem into a `tier1` outcome; this module forwards that as a revised decision plus a hint.
|
|
14
|
-
*
|
|
15
|
-
* @module dsh-remote-workspace/integration/agent-preflight
|
|
16
|
-
*/
|
|
17
|
-
import type { TransportPreflight } from '../app/transport-service.ts';
|
|
18
|
-
import type { ConnectionProfile } from '../domain/connection.ts';
|
|
19
|
-
import type { AgentInstallation } from '../infra/transport/agent-installer.ts';
|
|
20
|
-
/** What the preflight learned about one connection. */
|
|
21
|
-
export type AgentPreflightOutcome = {
|
|
22
|
-
readonly kind: 'agent';
|
|
23
|
-
readonly remotePath: string;
|
|
24
|
-
readonly platform: string;
|
|
25
|
-
readonly reused: boolean;
|
|
26
|
-
} | {
|
|
27
|
-
readonly kind: 'tier1';
|
|
28
|
-
readonly reason: string;
|
|
29
|
-
readonly hint: string;
|
|
30
|
-
};
|
|
31
|
-
/** Options for {@link createAgentPreflight}. */
|
|
32
|
-
export interface AgentPreflightOptions {
|
|
33
|
-
/** Run (or reuse a memoized) ladder for one connection. Injected so this module stays offline-testable. */
|
|
34
|
-
readonly ensure: (profile: ConnectionProfile, signal?: AbortSignal) => Promise<AgentInstallation>;
|
|
35
|
-
/** Optional sink so the connection UI can show what happened. */
|
|
36
|
-
readonly onOutcome?: (connectionId: string, outcome: AgentPreflightOutcome) => void;
|
|
37
|
-
/** Optional logger for the "we are not even trying" path; defaults to silence. */
|
|
38
|
-
readonly onSkipped?: (profile: ConnectionProfile) => void;
|
|
39
|
-
}
|
|
40
|
-
/** The preflight plus the installation facts the agent factory needs. */
|
|
41
|
-
export interface AgentPreflight {
|
|
42
|
-
/** Pass this to {@link TransportServiceOptions.preflight}. */
|
|
43
|
-
readonly preflight: TransportPreflight;
|
|
44
|
-
/** The installation for a connection, once the ladder approved one. */
|
|
45
|
-
installationFor(connectionId: string): AgentPreflightOutcome | undefined;
|
|
46
|
-
/** Forget one connection (its transport was released, or the user changed the connection). */
|
|
47
|
-
forget(connectionId: string): void;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Build the preflight.
|
|
51
|
-
*
|
|
52
|
-
* @param options - ladder plus optional sinks.
|
|
53
|
-
* @returns the preflight, an installation lookup, and a way to forget a connection.
|
|
54
|
-
*/
|
|
55
|
-
export declare function createAgentPreflight(options: AgentPreflightOptions): AgentPreflight;
|
|
56
|
-
//# sourceMappingURL=agent-preflight.d.ts.map
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The local anchor directory of a remote root.
|
|
3
|
-
*
|
|
4
|
-
* A remote root needs a **local** path to stand in for it, because a workspace path must exist on this machine
|
|
5
|
-
* (`root.ts` keeps that anchor on the `RootRef`). The platform then resolves that path with `realpath` when it
|
|
6
|
-
* opens a workspace on it, so the directory has to be **created**, not just computed:
|
|
7
|
-
*
|
|
8
|
-
* workspace create failed: workspace/invalid-path: cannot create a Workspace at
|
|
9
|
-
* "C:\\Users\\…\\.dsh\\ssh-dev\\roots\\腾讯云\\39d1e90c": ENOENT …
|
|
10
|
-
*
|
|
11
|
-
* That failure is what this module exists to prevent. It lives in its own file so the rule ("a remote root's
|
|
12
|
-
* anchor must exist") is in one place and testable without a real home directory (红线 3: 外部依赖走注入).
|
|
13
|
-
*
|
|
14
|
-
* @module dsh-remote-workspace/integration/anchor-directory
|
|
15
|
-
*/
|
|
16
|
-
import type { RootRef } from '../domain/root.ts';
|
|
17
|
-
/** The one filesystem call this module needs. */
|
|
18
|
-
export interface AnchorDirectoryIo {
|
|
19
|
-
/**
|
|
20
|
-
* Create a directory and its parents; a no-op when it already exists.
|
|
21
|
-
*
|
|
22
|
-
* The return value is ignored on purpose (`node:fs` answers with the first created path): this module only
|
|
23
|
-
* cares that the directory exists afterwards, so the seam stays as narrow as that fact.
|
|
24
|
-
*/
|
|
25
|
-
mkdir(path: string, options: {
|
|
26
|
-
readonly recursive: true;
|
|
27
|
-
}): Promise<unknown> | unknown;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Make one root's local anchor exist.
|
|
31
|
-
*
|
|
32
|
-
* Local roots are their own anchor (the directory is the user's own path, which the platform checks itself), and
|
|
33
|
-
* a remote root without an anchor simply has nothing to create - both cases return without touching the disk.
|
|
34
|
-
*
|
|
35
|
-
* @param root - the root that is about to become live.
|
|
36
|
-
* @param io - the injected filesystem.
|
|
37
|
-
* @returns nothing; a failure propagates so the caller decides whether it is fatal.
|
|
38
|
-
*/
|
|
39
|
-
export declare function ensureAnchorDirectory(root: Pick<RootRef, 'location' | 'anchorPath'>, io: AnchorDirectoryIo): Promise<void>;
|
|
40
|
-
/**
|
|
41
|
-
* The last path segment of a remote root's anchor: **the remote folder's own name**.
|
|
42
|
-
*
|
|
43
|
-
* Why it matters: the platform titles a new workspace with `defaultWorkspaceTitle(path)` =
|
|
44
|
-
* `basename(path)` (`dsh-workspace/lib/types/paths.js:26-29`), and the directory-flow contract only carries a
|
|
45
|
-
* `path` - there is no title parameter to pass (`DirectoryFlowOwnerProps`). So the segment this function
|
|
46
|
-
* returns **is** the name the user sees in the sidebar. A digest-only anchor made every remote workspace show
|
|
47
|
-
* up as `daf8c293`; this is what turns it into `stress_10`.
|
|
48
|
-
*
|
|
49
|
-
* Identity is *not* this function's job: the caller keeps a digest directory above it, so two remote paths with
|
|
50
|
-
* the same folder name still get two anchors (one canonical path per workspace).
|
|
51
|
-
*
|
|
52
|
-
* @param remotePath - absolute POSIX path on the remote host.
|
|
53
|
-
* @returns a segment that is safe to create on this machine and readable as a workspace name.
|
|
54
|
-
*/
|
|
55
|
-
export declare function anchorLeafName(remotePath: string): string;
|
|
56
|
-
//# sourceMappingURL=anchor-directory.d.ts.map
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-connection store of `SSH_ASKPASS` helpers.
|
|
3
|
-
*
|
|
4
|
-
* Why this exists as a module: preparing a helper is **async** (it reads the secret from the credential
|
|
5
|
-
* service and writes two files), but transport factories are **synchronous** and are also reached from
|
|
6
|
-
* `createAlias` during the install ladder. The two are joined here: something async calls
|
|
7
|
-
* {@link AskpassRegistry.prepare} first, and the factory then reads {@link AskpassRegistry.envFor}
|
|
8
|
-
* without awaiting.
|
|
9
|
-
*
|
|
10
|
-
* Three rules:
|
|
11
|
-
*
|
|
12
|
-
* 1. **One helper per connection.** Preparing twice returns the same handle, so a reconnect does not
|
|
13
|
-
* accumulate temp directories (and the secret is read once).
|
|
14
|
-
* 2. **Only password connections get one.** Key and agent connections never prompt; handing them
|
|
15
|
-
* `BatchMode=no` would let a misconfigured host block on an interactive prompt forever.
|
|
16
|
-
* 3. **A failure is a degradation, not a throw.** If the secret cannot be resolved the connection still
|
|
17
|
-
* works through the transports that do not need it; {@link AskpassRegistry.prepare} reports `undefined`
|
|
18
|
-
* and the caller keeps its previous behaviour.
|
|
19
|
-
*
|
|
20
|
-
* @module dsh-remote-workspace/integration/askpass-registry
|
|
21
|
-
*/
|
|
22
|
-
import { type AskpassHandle } from '../infra/transport/askpass.ts';
|
|
23
|
-
import type { ConnectionProfile } from '../domain/connection.ts';
|
|
24
|
-
/** Options for {@link createAskpassRegistry}. */
|
|
25
|
-
export interface AskpassRegistryOptions {
|
|
26
|
-
/** Resolve the connection's password. Throws when it is missing or the credential service is absent. */
|
|
27
|
-
readonly readSecret: (profile: ConnectionProfile) => Promise<string>;
|
|
28
|
-
/** Materialise one helper for a secret. */
|
|
29
|
-
readonly create: (secret: string) => Promise<AskpassHandle>;
|
|
30
|
-
/** Optional sink for "we could not prepare one" so the reason is not swallowed. */
|
|
31
|
-
readonly onUnavailable?: (connectionId: string, reason: string) => void;
|
|
32
|
-
}
|
|
33
|
-
/** The registry surface the composition root needs. */
|
|
34
|
-
export interface AskpassRegistry {
|
|
35
|
-
/**
|
|
36
|
-
* Ensure a helper exists for this connection.
|
|
37
|
-
*
|
|
38
|
-
* @param profile - the connection.
|
|
39
|
-
* @returns the environment to hand `ssh`, or `undefined` when this connection needs none or preparation
|
|
40
|
-
* failed (in which case the caller must keep its non-prompting behaviour).
|
|
41
|
-
*/
|
|
42
|
-
prepare(profile: ConnectionProfile): Promise<Record<string, string> | undefined>;
|
|
43
|
-
/** The environment prepared for this connection, if any. Synchronous, for transport factories. */
|
|
44
|
-
envFor(profile: ConnectionProfile): Record<string, string> | undefined;
|
|
45
|
-
/** Whether a connection is currently using a password helper. */
|
|
46
|
-
has(connectionId: string): boolean;
|
|
47
|
-
/** Drop one connection's helper (its transport was released, or the connection changed). */
|
|
48
|
-
forget(connectionId: string): Promise<void>;
|
|
49
|
-
/** Drop every helper (plugin unload). */
|
|
50
|
-
disposeAll(): Promise<void>;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Build the registry.
|
|
54
|
-
*
|
|
55
|
-
* @param options - injected secret reader, helper factory and optional sink.
|
|
56
|
-
* @returns the registry.
|
|
57
|
-
*/
|
|
58
|
-
export declare function createAskpassRegistry(options: AskpassRegistryOptions): AskpassRegistry;
|
|
59
|
-
//# sourceMappingURL=askpass-registry.d.ts.map
|