@tiphareth/dsh-hardssh 0.1.2 → 0.2.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.
- package/LICENSE +30 -0
- package/README.md +77 -50
- package/SKILLS.md +140 -0
- package/lib/base/index.js +95 -0
- package/lib/client.js +1171 -1321
- package/lib/fs.js +333 -416
- package/lib/index.js +5428 -2991
- package/lib/ledger-B-LXlftp.js +43 -0
- package/lib/ledger-D2ezq1iW.js +376 -0
- package/lib/model-Cp7f70Mb.js +5 -0
- package/lib/registry-CViuzKYI.js +421 -0
- package/lib/subprocess.js +92 -549
- package/lib/types/backend.d.ts +73 -78
- package/lib/types/base/capability.d.ts +4 -0
- package/lib/types/base/ledger-router.d.ts +42 -26
- package/lib/types/base/ledger.d.ts +30 -19
- package/lib/types/base/model.d.ts +17 -8
- package/lib/types/base/plugin.d.ts +11 -0
- package/lib/types/base/registry.d.ts +28 -31
- package/lib/types/base/router.d.ts +0 -7
- package/lib/types/client/api.d.ts +8 -8
- package/lib/types/client/connect-host.d.ts +13 -0
- package/lib/types/client/index.d.ts +6 -4
- package/lib/types/client/locales.d.ts +15 -1
- package/lib/types/client/session-connect-gate.d.ts +71 -0
- package/lib/types/client/ssh/api.d.ts +11 -9
- package/lib/types/client/ssh/apply.d.ts +14 -8
- package/lib/types/client/ssh/locales.d.ts +8 -1
- package/lib/types/client/ssh/ops-tab.d.ts +34 -0
- package/lib/types/client/ssh/panel/ClusterTab.d.ts +3 -1
- package/lib/types/client/ssh/panel/ConnectionErrorDialog.d.ts +8 -0
- package/lib/types/client/ssh/panel/SessionSecretDialog.d.ts +4 -1
- package/lib/types/client/ssh/panel/SshPanel.d.ts +8 -8
- package/lib/types/client/ssh/panel/TerminalTab.d.ts +3 -5
- package/lib/types/client/ssh/panel/TransferTab.d.ts +5 -1
- package/lib/types/client/ssh/panel/TunnelsTab.d.ts +3 -1
- package/lib/types/client/ssh/session-target.d.ts +17 -0
- package/lib/types/client/state.d.ts +4 -1
- package/lib/types/client/workspace-panel-entry.d.ts +30 -0
- package/lib/types/client/workspace-panel.d.ts +24 -0
- package/lib/types/client-http.d.ts +77 -6
- package/lib/types/core.d.ts +5 -16
- package/lib/types/fs.d.ts +42 -25
- package/lib/types/index.d.ts +138 -9
- package/lib/types/ledger.d.ts +16 -111
- package/lib/types/protocol.d.ts +0 -5
- package/lib/types/providers/index.d.ts +6 -3
- package/lib/types/providers/local/provider.d.ts +83 -23
- package/lib/types/providers/ssh/provider.d.ts +24 -57
- package/lib/types/remote/environment.d.ts +2 -0
- package/lib/types/remote/remote-fs.d.ts +12 -1
- package/lib/types/remote/remote-process.d.ts +22 -1
- package/lib/types/remote/remote-subprocess.d.ts +13 -1
- package/lib/types/remote/remote-terminal.d.ts +4 -0
- package/lib/types/remote-search.d.ts +3 -3
- package/lib/types/routes.d.ts +36 -8
- package/lib/types/runtime/dsh-capabilities.d.ts +18 -0
- package/lib/types/runtime/workspace-core.d.ts +73 -38
- package/lib/types/runtime/workspace-migration.d.ts +113 -0
- package/lib/types/ssh/connection/lease.d.ts +23 -0
- package/lib/types/ssh/connection/manager.d.ts +359 -0
- package/lib/types/ssh/connection/pool.d.ts +8 -1
- package/lib/types/ssh/engine.d.ts +97 -267
- package/lib/types/ssh/known-hosts.d.ts +22 -5
- package/lib/types/ssh/local-transfer-policy.d.ts +20 -0
- package/lib/types/ssh/plugin.d.ts +10 -15
- package/lib/types/ssh/protocol.d.ts +18 -0
- package/lib/types/ssh/routes.d.ts +15 -7
- package/lib/types/ssh/sftp/service.d.ts +216 -0
- package/lib/types/ssh/store.d.ts +20 -8
- package/lib/types/ssh/terminal/service.d.ts +49 -0
- package/lib/types/ssh/tunnel/service.d.ts +26 -0
- package/lib/types/ssh/vault.d.ts +51 -4
- package/lib/types/subprocess.d.ts +13 -21
- package/lib/types/switch/switch-fs.d.ts +52 -2
- package/lib/types/switch/switch-subprocess.d.ts +32 -3
- package/lib/types/tools.d.ts +7 -6
- package/lib/types/workspace-tool-ops.d.ts +33 -0
- package/lib/types/workspace.d.ts +18 -0
- package/lib/vault-3gpWct2Q.js +559 -0
- package/lib/workspace.js +2 -0
- package/package.json +59 -26
- package/scripts/export-legacy-workspaces.mjs +136 -0
- package/src/backend.ts +155 -483
- package/src/base/capability.ts +4 -0
- package/src/base/ledger-router.ts +214 -65
- package/src/base/ledger.ts +175 -48
- package/src/base/model.ts +18 -8
- package/src/base/plugin.ts +15 -4
- package/src/base/registry.ts +49 -41
- package/src/base/router.ts +0 -8
- package/src/client/api.ts +8 -36
- package/src/client/connect-host.ts +199 -0
- package/src/client/directory-flow.tsx +91 -36
- package/src/client/index.ts +56 -53
- package/src/client/locales.ts +14 -0
- package/src/client/session-connect-gate.ts +163 -0
- package/src/client/ssh/api.ts +245 -108
- package/src/client/ssh/apply.ts +23 -24
- package/src/client/ssh/locales.ts +16 -2
- package/src/client/ssh/ops-tab.tsx +79 -0
- package/src/client/ssh/panel/ClusterTab.tsx +9 -20
- package/src/client/ssh/panel/ConnectionErrorDialog.tsx +33 -0
- package/src/client/ssh/panel/SessionSecretDialog.tsx +5 -1
- package/src/client/ssh/panel/SshPanel.tsx +81 -77
- package/src/client/ssh/panel/TerminalTab.tsx +18 -37
- package/src/client/ssh/panel/TransferTab.tsx +43 -33
- package/src/client/ssh/panel/TunnelsTab.tsx +9 -26
- package/src/client/ssh/panel/panel.module.css +90 -127
- package/src/client/ssh/session-target.ts +69 -0
- package/src/client/state.ts +9 -1
- package/src/client/workspace-badges.ts +99 -96
- package/src/client/workspace-panel-entry.tsx +91 -0
- package/src/client/workspace-panel.tsx +227 -0
- package/src/client/workspace.module.css +76 -2
- package/src/client-http.ts +118 -18
- package/src/core.ts +37 -47
- package/src/fs.ts +171 -85
- package/src/index.ts +535 -244
- package/src/ledger.ts +62 -416
- package/src/protocol.ts +0 -6
- package/src/providers/index.ts +8 -4
- package/src/providers/local/provider.ts +259 -87
- package/src/providers/ssh/provider.ts +158 -155
- package/src/remote/environment.ts +22 -1
- package/src/remote/remote-fs.ts +123 -34
- package/src/remote/remote-process.ts +265 -203
- package/src/remote/remote-subprocess.ts +61 -18
- package/src/remote/remote-terminal.ts +106 -21
- package/src/remote-search.ts +122 -26
- package/src/routes.ts +416 -395
- package/src/runtime/dsh-capabilities.ts +19 -0
- package/src/runtime/workspace-core.ts +171 -88
- package/src/runtime/workspace-migration.ts +472 -0
- package/src/ssh/connection/lease.ts +35 -0
- package/src/ssh/connection/manager.ts +1083 -0
- package/src/ssh/connection/pool.ts +341 -275
- package/src/ssh/engine.ts +269 -1477
- package/src/ssh/known-hosts.ts +42 -18
- package/src/ssh/local-transfer-policy.ts +83 -0
- package/src/ssh/plugin.ts +20 -19
- package/src/ssh/protocol.ts +22 -1
- package/src/ssh/routes.ts +419 -187
- package/src/ssh/sftp/service.ts +967 -0
- package/src/ssh/store.ts +169 -72
- package/src/ssh/terminal/service.ts +177 -0
- package/src/ssh/tools.ts +42 -20
- package/src/ssh/tunnel/service.ts +217 -0
- package/src/ssh/vault.ts +245 -91
- package/src/subprocess.ts +88 -71
- package/src/switch/switch-fs.ts +141 -10
- package/src/switch/switch-subprocess.ts +80 -6
- package/src/tools.ts +217 -221
- package/src/workspace-tool-ops.ts +101 -0
- package/src/workspace.ts +52 -0
- package/lib/environment-BL1jddfB.js +0 -449
- package/lib/switch-fs-CAJpFY9C.js +0 -193
- package/lib/switch-fs-RrZtG2gv.js +0 -210
- package/lib/types/client/manager-button.d.ts +0 -32
- package/lib/types/client/ssh/mount.d.ts +0 -13
- package/lib/types/client/ssh/panel/HostsTab.d.ts +0 -10
- package/lib/types/client/ssh/panel/controller.d.ts +0 -23
- package/lib/types/client/ssh/sidebar-entry.d.ts +0 -25
- package/lib/types/client/workspace-gate.d.ts +0 -15
- package/lib/types/remote-runner.d.ts +0 -83
- package/lib/types/seam-state.d.ts +0 -69
- package/src/client/manager-button.tsx +0 -269
- package/src/client/ssh/mount.tsx +0 -83
- package/src/client/ssh/panel/HostsTab.tsx +0 -236
- package/src/client/ssh/panel/controller.ts +0 -48
- package/src/client/ssh/sidebar-entry.ts +0 -123
- package/src/client/workspace-gate.ts +0 -225
- package/src/remote-runner.ts +0 -201
- package/src/seam-state.ts +0 -185
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { Client, type ConnectConfig } from 'ssh2';
|
|
2
|
+
import { type SshConnectionService } from './pool.ts';
|
|
3
|
+
import { HostKeyPolicy, type HostKeyCheck, type KnownHostsStore } from '../known-hosts.ts';
|
|
4
|
+
import type { ClusterResult, ExecResult, SshHostEntry, SshHostSummary, TestResult } from '../protocol.ts';
|
|
5
|
+
import type { HostStoreView } from '../../core.ts';
|
|
6
|
+
export interface SshInvalidateOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Also invalidate hosts whose ProxyJump chain depends, directly or
|
|
9
|
+
* transitively, on the changed alias.
|
|
10
|
+
*/
|
|
11
|
+
includeDependents?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* drain: reject/reconnect subsequent acquisitions, allow existing leases
|
|
14
|
+
* to complete before closing their transport.
|
|
15
|
+
* force: close the current transport immediately.
|
|
16
|
+
*/
|
|
17
|
+
mode?: 'drain' | 'force';
|
|
18
|
+
}
|
|
19
|
+
/** Default engine knobs. */
|
|
20
|
+
export interface EngineOptions {
|
|
21
|
+
/** Connections idle longer than this are closed (ms). */
|
|
22
|
+
idleTimeoutMs?: number;
|
|
23
|
+
/** SSH handshake timeout (ms). */
|
|
24
|
+
connectTimeoutMs?: number;
|
|
25
|
+
/** Keepalive ping interval (ms). */
|
|
26
|
+
keepaliveIntervalMs?: number;
|
|
27
|
+
/** Cap on captured stdout/stderr bytes per exec (ms). */
|
|
28
|
+
maxOutputBytes?: number;
|
|
29
|
+
/** Default exec timeout (ms). */
|
|
30
|
+
defaultExecTimeoutMs?: number;
|
|
31
|
+
/** Default cluster concurrency. */
|
|
32
|
+
defaultMaxWorkers?: number;
|
|
33
|
+
/** SFTP concurrent channel count for transfers. */
|
|
34
|
+
sftpConcurrency?: number;
|
|
35
|
+
/** Deadline for opening an SFTP subsystem channel. */
|
|
36
|
+
sftpOpenTimeoutMs?: number;
|
|
37
|
+
/** Per-request SFTP callback deadline. */
|
|
38
|
+
sftpOperationTimeoutMs?: number;
|
|
39
|
+
/** Whole-file read/write stream inactivity deadline. */
|
|
40
|
+
sftpReadTimeoutMs?: number;
|
|
41
|
+
/** Maximum bytes buffered by readFile before it aborts. */
|
|
42
|
+
maxReadFileBytes?: number;
|
|
43
|
+
/** Upload/download inactivity deadline, reset by progress. */
|
|
44
|
+
sftpTransferIdleTimeoutMs?: number;
|
|
45
|
+
/** Absolute budget for one recursive rm traversal. */
|
|
46
|
+
sftpRecursiveRmTimeoutMs?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Optional server-host-key algorithm whitelist (e.g. ['ssh-ed25519']).
|
|
49
|
+
* Single authoritative source: connection building reads it from these
|
|
50
|
+
* options (EngineDeps deliberately has no duplicate).
|
|
51
|
+
*/
|
|
52
|
+
hostKeyAlgorithms?: string[];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Optional engine dependencies for host-key TOFU and secret resolution.
|
|
56
|
+
* All fields are optional and their absence preserves the pre-security
|
|
57
|
+
* behavior exactly (plaintext inline auth, no host verification) — the
|
|
58
|
+
* existing tests and call sites keep working unchanged.
|
|
59
|
+
*/
|
|
60
|
+
export interface EngineDeps {
|
|
61
|
+
/** Known-hosts trust store; when set, connections require a trusted host key. */
|
|
62
|
+
knownHosts?: KnownHostsStore;
|
|
63
|
+
/** Fingerprint check policy (defaults to a HostKeyPolicy over knownHosts). */
|
|
64
|
+
hostKeyPolicy?: HostKeyPolicy;
|
|
65
|
+
/**
|
|
66
|
+
* Secret resolution for one entry. Absent: read `password`/`passphrase`
|
|
67
|
+
* inline from the entry (plaintext store / test compatibility).
|
|
68
|
+
*/
|
|
69
|
+
resolveSecrets?: (entry: SshHostEntry) => Promise<ResolvedAuthDeps>;
|
|
70
|
+
/** Final output/error redactor (typically the unlocked credential vault). */
|
|
71
|
+
redactOutput?: (text: string) => string;
|
|
72
|
+
}
|
|
73
|
+
/** The resolved-auth shape passed into connect config building (vault-aware). */
|
|
74
|
+
export interface ResolvedAuthDeps {
|
|
75
|
+
kind: SshHostEntry['auth']['kind'];
|
|
76
|
+
keyPath?: string;
|
|
77
|
+
password?: string;
|
|
78
|
+
passphrase?: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Thrown when a connection needs a password/passphrase that is not yet
|
|
82
|
+
* available in this session (secretStorage='none' and the user hasn't entered
|
|
83
|
+
* it yet). The GUI intercepts this and prompts for the credential, then
|
|
84
|
+
* injects it via engine.setSessionPassword and retries.
|
|
85
|
+
*/
|
|
86
|
+
export declare class NeedsPasswordError extends Error {
|
|
87
|
+
/** Which secret the connection needs: 'password' or 'passphrase'. */
|
|
88
|
+
readonly secret: 'password' | 'passphrase';
|
|
89
|
+
constructor(alias: string, secret: 'password' | 'passphrase');
|
|
90
|
+
}
|
|
91
|
+
export declare const DEFAULTS: Required<Omit<EngineOptions, 'hostKeyAlgorithms'>> & Pick<EngineOptions, 'hostKeyAlgorithms'>;
|
|
92
|
+
/**
|
|
93
|
+
* How much an operation may be retried:
|
|
94
|
+
* - never: one acquisition + one operation attempt.
|
|
95
|
+
* - connect-only: connection acquisition may be retried, but once the
|
|
96
|
+
* operation function starts it is invoked at most once (default).
|
|
97
|
+
* - idempotent: the operation may also be retried until it calls
|
|
98
|
+
* markCommitted() (i.e. while the exec channel is still opening).
|
|
99
|
+
*
|
|
100
|
+
* SFTP operations must never use 'idempotent': they have no commit point, so
|
|
101
|
+
* a replay after a mid-flight timeout would duplicate a remote write.
|
|
102
|
+
*/
|
|
103
|
+
export type RetryPolicy = 'never' | 'connect-only' | 'idempotent';
|
|
104
|
+
/** Options for a one-shot remote command. */
|
|
105
|
+
export interface ExecOptions {
|
|
106
|
+
timeoutMs?: number;
|
|
107
|
+
retry?: RetryPolicy;
|
|
108
|
+
signal?: AbortSignal;
|
|
109
|
+
}
|
|
110
|
+
/** Internal options for withClient(). */
|
|
111
|
+
export interface WithClientOptions {
|
|
112
|
+
/** Total acquire+operation attempt budget (default 3, capped at 1 for 'never'). */
|
|
113
|
+
attempts?: number;
|
|
114
|
+
retryPolicy?: RetryPolicy;
|
|
115
|
+
signal?: AbortSignal;
|
|
116
|
+
}
|
|
117
|
+
/** Lets an operation declare the point after which replay is unsafe. */
|
|
118
|
+
export interface OperationControl {
|
|
119
|
+
markCommitted(): void;
|
|
120
|
+
/**
|
|
121
|
+
* The caller's abort signal for THIS operation (undefined = not cancellable).
|
|
122
|
+
* Operations that can cancel their own request (`exec` closing its channel,
|
|
123
|
+
* an SFTP read stream) listen here, so an abort does not have to retire the
|
|
124
|
+
* whole shared transport out from under concurrent holders.
|
|
125
|
+
*/
|
|
126
|
+
readonly signal?: AbortSignal;
|
|
127
|
+
}
|
|
128
|
+
export interface HostKeyOutcome {
|
|
129
|
+
alias: string;
|
|
130
|
+
check: HostKeyCheck;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Half-open channels tolerated on one pooled transport before it is retired.
|
|
134
|
+
* Deliberately well below OpenSSH's default `MaxSessions` (10) so the budget is
|
|
135
|
+
* never actually exhausted.
|
|
136
|
+
*/
|
|
137
|
+
export declare const MAX_LEAKED_CHANNELS = 4;
|
|
138
|
+
/**
|
|
139
|
+
* Resolve the ssh-agent socket to offer to ssh2 (zero-input key auth,
|
|
140
|
+
* VSCode-style): `$SSH_AUTH_SOCK` — the standard OpenSSH agent socket (also
|
|
141
|
+
* exported by Git for Windows' ssh-agent and WSL). Deliberately NOT probing
|
|
142
|
+
* named pipes (Pageant / Windows OpenSSH agent): an absent pipe makes ssh2's
|
|
143
|
+
* agent query stall the whole handshake until readyTimeout instead of
|
|
144
|
+
* falling through to the next method. Keep Pageant compatibility for a
|
|
145
|
+
* future explicit opt-in. An agent that yields no keys makes ssh2 fall
|
|
146
|
+
* through to the configured methods (privateKey → password), so enabling it
|
|
147
|
+
* when a socket is present is safe. Exported for tests.
|
|
148
|
+
*/
|
|
149
|
+
export declare function sshAgentConfig(): string | undefined;
|
|
150
|
+
/** Build the ssh2 connect config for one entry (key read from disk). The
|
|
151
|
+
* timeout/keepalive knobs come from EngineOptions so they actually take
|
|
152
|
+
* effect instead of being hard-coded. Exported for tests. */
|
|
153
|
+
export declare function buildConnectConfig(entry: SshHostEntry, options: Pick<Required<EngineOptions>, 'connectTimeoutMs' | 'keepaliveIntervalMs'>, sock?: ConnectConfig['sock'], buildContext?: {
|
|
154
|
+
hostKeyPolicy?: HostKeyPolicy;
|
|
155
|
+
hostKeyAlgorithms?: string[];
|
|
156
|
+
/** Writes the verified/refused outcome back to the caller's capture slot. */
|
|
157
|
+
setOutcome?: (value: HostKeyOutcome) => void;
|
|
158
|
+
/** Vault-resolved authentication (overrides entry.auth secrets). */
|
|
159
|
+
authOverride?: ResolvedAuthDeps;
|
|
160
|
+
}): ConnectConfig;
|
|
161
|
+
/**
|
|
162
|
+
* Host-side hooks the connection manager needs but must not own. Injected by
|
|
163
|
+
* the facade so every resource keeps exactly one dispose point and the manager
|
|
164
|
+
* never imports the facade back (no cycle).
|
|
165
|
+
*/
|
|
166
|
+
export interface ConnectionManagerAccess {
|
|
167
|
+
/** A pooled client is being torn down — drop its cached SFTP channel. */
|
|
168
|
+
onClientDisposed?(client: Client, error: Error): void;
|
|
169
|
+
/** Vault-aware output guard applied before session-secret masking. */
|
|
170
|
+
redactOutput?(text: string): string;
|
|
171
|
+
/**
|
|
172
|
+
* The one-command runner (the facade's `exec`). Injected rather than
|
|
173
|
+
* reimplemented here so cluster() fans out through the SAME method surface
|
|
174
|
+
* callers/spies observe on the engine, and `test` reuses it verbatim.
|
|
175
|
+
*/
|
|
176
|
+
executor?: (alias: string, command: string, options?: ExecOptions) => Promise<ExecResult>;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Owns the pooled SSH connections and everything layered on them: per-alias
|
|
180
|
+
* ProxyJump connect chains, session-scoped secrets and output redaction, the
|
|
181
|
+
* retry/replay policy, and the command surface (exec/cluster/test).
|
|
182
|
+
*
|
|
183
|
+
* Dispose order lives in the facade: tunnels, standalone terminal transports
|
|
184
|
+
* and SFTP channels are closed first, so a resource is always closed before
|
|
185
|
+
* the transport that carries it.
|
|
186
|
+
*/
|
|
187
|
+
export declare class ConnectionManager {
|
|
188
|
+
private readonly store;
|
|
189
|
+
private readonly opts;
|
|
190
|
+
private readonly access;
|
|
191
|
+
private readonly executor;
|
|
192
|
+
private readonly connectionPool;
|
|
193
|
+
private readonly deps;
|
|
194
|
+
private readonly hostKeyPolicy;
|
|
195
|
+
/**
|
|
196
|
+
* Session-scoped secrets (secretStorage='none'): keyed by alias, populated
|
|
197
|
+
* by the GUI on first connect, used by connectChain's resolve step, and
|
|
198
|
+
* cleared on dispose. Never persisted.
|
|
199
|
+
*/
|
|
200
|
+
private readonly sessionPasswords;
|
|
201
|
+
/**
|
|
202
|
+
* alias → every secret value registered for it in this process, including
|
|
203
|
+
* ones replaced by a newer value. Released with the alias's connection, so a
|
|
204
|
+
* rotated password stays redacted while the old connection can still echo it.
|
|
205
|
+
*/
|
|
206
|
+
private readonly secretHistory;
|
|
207
|
+
private readonly sensitiveOutputs;
|
|
208
|
+
/** Per-alias count of channels that never acknowledged close after a timeout. */
|
|
209
|
+
private readonly leakedChannels;
|
|
210
|
+
/**
|
|
211
|
+
* @param store - the host config store.
|
|
212
|
+
* @param options - engine knobs (defaults applied after validation).
|
|
213
|
+
* @param deps - optional security deps (host-key TOFU, secret resolution).
|
|
214
|
+
* Absent → pre-security behavior (inline auth, no host verification).
|
|
215
|
+
* @param access - optional host-side hooks (SFTP cache eviction, redactor).
|
|
216
|
+
*/
|
|
217
|
+
constructor(store: HostStoreView, options?: EngineOptions, deps?: EngineDeps, access?: ConnectionManagerAccess);
|
|
218
|
+
/** Validate the engine knobs once, so the connection and SFTP components
|
|
219
|
+
* read one frozen source of truth. */
|
|
220
|
+
static resolveOptions(options?: EngineOptions): Required<Omit<EngineOptions, 'hostKeyAlgorithms'>> & Pick<EngineOptions, 'hostKeyAlgorithms'>;
|
|
221
|
+
/** Aliases with a live pooled transport right now (for connection-state
|
|
222
|
+
* indicators — the GUI badge colors bound workspaces by it). */
|
|
223
|
+
connectedAliases(): string[];
|
|
224
|
+
/**
|
|
225
|
+
* Record one channel that never acknowledged its close after a hard timeout.
|
|
226
|
+
*
|
|
227
|
+
* ssh2 cannot cancel a request, so a peer that ignores KILL + close leaves a
|
|
228
|
+
* half-open channel counted against the server's `MaxSessions` budget. Once
|
|
229
|
+
* enough of them accumulate the pooled transport is DRAINED — not force-
|
|
230
|
+
* closed — so concurrent holders finish their work while new operations open
|
|
231
|
+
* a fresh generation (the pool reaps the old record when its last lease is
|
|
232
|
+
* released).
|
|
233
|
+
*
|
|
234
|
+
* The counter is per alias and is decremented again by
|
|
235
|
+
* {@link noteChannelClosed} when a late acknowledgement finally arrives, so a
|
|
236
|
+
* merely slow peer cannot accumulate its way to a retirement.
|
|
237
|
+
*
|
|
238
|
+
* @param alias - the host the channel belonged to.
|
|
239
|
+
* @returns true when the transport was retired.
|
|
240
|
+
*/
|
|
241
|
+
noteLeakedChannel(alias: string): boolean;
|
|
242
|
+
/** A timed-out channel finally acknowledged its close: un-count it. */
|
|
243
|
+
noteChannelClosed(alias: string): void;
|
|
244
|
+
/** Pooled connections owned by this manager (the tunnel component's narrow
|
|
245
|
+
* access seam, and the public `connections` getter on the facade). */
|
|
246
|
+
get connections(): SshConnectionService;
|
|
247
|
+
/** The validated engine knobs. The facade's exec reads its output cap and
|
|
248
|
+
* default budget here, so defaults keep exactly one source of truth. */
|
|
249
|
+
get resolvedOptions(): Required<Omit<EngineOptions, 'hostKeyAlgorithms'>> & Pick<EngineOptions, 'hostKeyAlgorithms'>;
|
|
250
|
+
/**
|
|
251
|
+
* Provide a secret for `alias` for THIS session only (never persisted).
|
|
252
|
+
* Used by the GUI when a connection needs a password/passphrase under
|
|
253
|
+
* secretStorage='none'. Once set, pooled connections reuse it until the
|
|
254
|
+
* session ends or clearSessionSecrets() is called.
|
|
255
|
+
*/
|
|
256
|
+
setSessionPassword(alias: string, secret: {
|
|
257
|
+
password?: string;
|
|
258
|
+
passphrase?: string;
|
|
259
|
+
}): void;
|
|
260
|
+
/** Read the session secret for one alias (undefined = not provided yet). */
|
|
261
|
+
getSessionPassword(alias: string): {
|
|
262
|
+
password?: string;
|
|
263
|
+
passphrase?: string;
|
|
264
|
+
} | undefined;
|
|
265
|
+
/**
|
|
266
|
+
* Drop ONE alias's session secret. Called when its pooled connection is
|
|
267
|
+
* retired: the credential is tied to the connection's lifetime, so an idle
|
|
268
|
+
* sweep must not leave it usable for the rest of the process.
|
|
269
|
+
*/
|
|
270
|
+
forgetSessionPassword(alias: string): void;
|
|
271
|
+
/** Drop every session secret (e.g. on secretStorage change / lock). */
|
|
272
|
+
clearSessionSecrets(): void;
|
|
273
|
+
/** Recompute the redaction set from every secret still in force, including
|
|
274
|
+
* values replaced by a newer one on the same alias (their connection may
|
|
275
|
+
* still be alive), so dropping one alias never un-redacts another's. */
|
|
276
|
+
private rebuildSensitiveOutputs;
|
|
277
|
+
/** Redact credentials known to this session and the optional vault guard. */
|
|
278
|
+
redact(text: string): string;
|
|
279
|
+
/** Secret-free host list (filtered by the optional query). */
|
|
280
|
+
list(query?: string): SshHostSummary[];
|
|
281
|
+
/** One host summary by alias. */
|
|
282
|
+
find(alias: string): SshHostSummary | undefined;
|
|
283
|
+
/**
|
|
284
|
+
* Retire the pooled connection for one alias.
|
|
285
|
+
*
|
|
286
|
+
* ConnectionPool knows nothing about HostStore or ProxyJump configuration
|
|
287
|
+
* (every target owns its complete jump chain, no hop records are shared),
|
|
288
|
+
* so dependent-host expansion belongs here: with includeDependents the
|
|
289
|
+
* transitive reverse ProxyJump closure of `alias` is invalidated too.
|
|
290
|
+
*/
|
|
291
|
+
invalidate(alias: string, options?: SshInvalidateOptions): void;
|
|
292
|
+
/**
|
|
293
|
+
* Run `fn` with a live client for `alias`.
|
|
294
|
+
*
|
|
295
|
+
* Acquisition retry and operation replay are deliberately separate:
|
|
296
|
+
* - never: one acquire + one operation attempt.
|
|
297
|
+
* - connect-only: acquire may be retried, but fn is invoked at most once —
|
|
298
|
+
* once fn starts, the remote may already have observed the request, so
|
|
299
|
+
* replay could duplicate non-idempotent work.
|
|
300
|
+
* - idempotent: fn may be retried until it calls control.markCommitted()
|
|
301
|
+
* (exec marks this when the server accepted the channel).
|
|
302
|
+
*
|
|
303
|
+
* A failed operation retires the transport via lease.markBroken(); the
|
|
304
|
+
* lease is always released before the next acquire attempt.
|
|
305
|
+
*/
|
|
306
|
+
withClient<T>(alias: string, fn: (client: Client, control: OperationControl) => Promise<T>, options?: WithClientOptions): Promise<T>;
|
|
307
|
+
/** Resolve an entry's authentication for one connect: session password
|
|
308
|
+
* table first (secretStorage='none'), then deps.resolveSecrets (vault),
|
|
309
|
+
* then the inline store entry; when a password/passphrase is required but
|
|
310
|
+
* unavailable, throw NeedsPasswordError for the GUI to prompt. */
|
|
311
|
+
resolveEntryAuth(entry: SshHostEntry): Promise<ResolvedAuthDeps | undefined>;
|
|
312
|
+
/**
|
|
313
|
+
* Build one full jump chain for an entry: hop clients connected through in
|
|
314
|
+
* order, each forwarding a stream to the next destination, ending with the
|
|
315
|
+
* target client. Shared by the pool and standalone shell sessions.
|
|
316
|
+
*/
|
|
317
|
+
connectChain(entry: SshHostEntry, signal?: AbortSignal): Promise<{
|
|
318
|
+
client: Client;
|
|
319
|
+
hops: Client[];
|
|
320
|
+
}>;
|
|
321
|
+
/**
|
|
322
|
+
* Run one command against many hosts concurrently.
|
|
323
|
+
*
|
|
324
|
+
* Per-host failures carry the typed, secret-free fields declared on
|
|
325
|
+
* `ClusterResult` (`code`, and `secret` / `hostKeyFingerprint` / `expected` /
|
|
326
|
+
* `actual` where applicable) alongside the legacy `error` string, so
|
|
327
|
+
* automation can branch on a stable code and the GUI can open the right
|
|
328
|
+
* credential/fingerprint dialog without parsing a localized message.
|
|
329
|
+
*/
|
|
330
|
+
cluster(options: {
|
|
331
|
+
command: string;
|
|
332
|
+
aliases?: string[];
|
|
333
|
+
environment?: string;
|
|
334
|
+
tags?: string[];
|
|
335
|
+
timeoutMs?: number;
|
|
336
|
+
maxWorkers?: number;
|
|
337
|
+
/** Aborts every per-host run when the caller disconnects. */
|
|
338
|
+
signal?: AbortSignal;
|
|
339
|
+
}): Promise<ClusterResult[]>;
|
|
340
|
+
/**
|
|
341
|
+
* Classify one per-host cluster failure. Keeps the legacy `error` string for
|
|
342
|
+
* backward compatibility while adding the typed reason: a NeedsPassword /
|
|
343
|
+
* host-key failure is interactive in the GUI, and automation needs a stable
|
|
344
|
+
* code. The message is redacted because connect/resolve errors can embed a
|
|
345
|
+
* credential; the added fields are fingerprints and enum values only.
|
|
346
|
+
*/
|
|
347
|
+
clusterFailure(alias: string, error: unknown): ClusterResult;
|
|
348
|
+
/** Probe connectivity: connect, run `true`, close. Typed errors the GUI
|
|
349
|
+
* must react to (host-key TOFU, session password) are NOT flattened into
|
|
350
|
+
* a plain message — callers (routes → panel / workspace gate) key their
|
|
351
|
+
* interactive dialogs on the typed error. Everything else (unreachable,
|
|
352
|
+
* timeout, auth failure) returns a failed result. */
|
|
353
|
+
test(alias: string, signal?: AbortSignal): Promise<TestResult>;
|
|
354
|
+
/** Close every pooled connection and wipe the in-memory session password
|
|
355
|
+
* table. The facade calls this LAST, after tunnels, terminal transports and
|
|
356
|
+
* SFTP channels are already closed (secrets are never persisted anywhere). */
|
|
357
|
+
disposeSensitive(): void;
|
|
358
|
+
}
|
|
359
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -18,12 +18,19 @@ export interface SshConnectionService {
|
|
|
18
18
|
export interface ConnectionPoolOptions {
|
|
19
19
|
idleTimeoutMs: number;
|
|
20
20
|
/** Open a fresh SSH connection (with jump chain) for one alias. */
|
|
21
|
-
connect(alias: string): Promise<{
|
|
21
|
+
connect(alias: string, signal?: AbortSignal): Promise<{
|
|
22
22
|
client: Client;
|
|
23
23
|
hops: Client[];
|
|
24
24
|
}>;
|
|
25
25
|
/** Called when a pooled connection is torn down (e.g. drop SFTP cache). */
|
|
26
26
|
onDispose?(client: Client): void;
|
|
27
|
+
/**
|
|
28
|
+
* Called once when an alias's pooled transport is retired (idle sweep,
|
|
29
|
+
* invalidation, shutdown). Session-scoped credentials are tied to the pooled
|
|
30
|
+
* connection's lifetime, so this is where they are dropped — otherwise a
|
|
31
|
+
* secret entered for one connection would outlive it for the whole process.
|
|
32
|
+
*/
|
|
33
|
+
onRetire?(alias: string): void;
|
|
27
34
|
}
|
|
28
35
|
/**
|
|
29
36
|
* Per-alias connection pool with explicit leases. A connection is swept only
|