@tiphareth/dsh-hardssh 0.1.2 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/LICENSE +30 -0
  2. package/README.md +49 -25
  3. package/SKILLS.md +140 -0
  4. package/lib/base/index.js +95 -0
  5. package/lib/client.js +1171 -1321
  6. package/lib/fs.js +307 -416
  7. package/lib/index.js +5428 -2991
  8. package/lib/ledger-B-LXlftp.js +43 -0
  9. package/lib/ledger-D2ezq1iW.js +376 -0
  10. package/lib/model-Cp7f70Mb.js +5 -0
  11. package/lib/registry-CViuzKYI.js +421 -0
  12. package/lib/subprocess.js +92 -549
  13. package/lib/types/backend.d.ts +73 -78
  14. package/lib/types/base/capability.d.ts +4 -0
  15. package/lib/types/base/ledger-router.d.ts +42 -26
  16. package/lib/types/base/ledger.d.ts +30 -19
  17. package/lib/types/base/model.d.ts +17 -8
  18. package/lib/types/base/plugin.d.ts +11 -0
  19. package/lib/types/base/registry.d.ts +28 -31
  20. package/lib/types/base/router.d.ts +0 -7
  21. package/lib/types/client/api.d.ts +8 -8
  22. package/lib/types/client/connect-host.d.ts +13 -0
  23. package/lib/types/client/index.d.ts +6 -4
  24. package/lib/types/client/locales.d.ts +15 -1
  25. package/lib/types/client/session-connect-gate.d.ts +71 -0
  26. package/lib/types/client/ssh/api.d.ts +11 -9
  27. package/lib/types/client/ssh/apply.d.ts +14 -8
  28. package/lib/types/client/ssh/locales.d.ts +8 -1
  29. package/lib/types/client/ssh/ops-tab.d.ts +34 -0
  30. package/lib/types/client/ssh/panel/ClusterTab.d.ts +3 -1
  31. package/lib/types/client/ssh/panel/ConnectionErrorDialog.d.ts +8 -0
  32. package/lib/types/client/ssh/panel/SessionSecretDialog.d.ts +4 -1
  33. package/lib/types/client/ssh/panel/SshPanel.d.ts +8 -8
  34. package/lib/types/client/ssh/panel/TerminalTab.d.ts +3 -5
  35. package/lib/types/client/ssh/panel/TransferTab.d.ts +5 -1
  36. package/lib/types/client/ssh/panel/TunnelsTab.d.ts +3 -1
  37. package/lib/types/client/ssh/session-target.d.ts +17 -0
  38. package/lib/types/client/state.d.ts +4 -1
  39. package/lib/types/client/workspace-panel-entry.d.ts +30 -0
  40. package/lib/types/client/workspace-panel.d.ts +24 -0
  41. package/lib/types/client-http.d.ts +77 -6
  42. package/lib/types/core.d.ts +5 -16
  43. package/lib/types/fs.d.ts +18 -25
  44. package/lib/types/index.d.ts +138 -9
  45. package/lib/types/ledger.d.ts +16 -111
  46. package/lib/types/protocol.d.ts +0 -5
  47. package/lib/types/providers/index.d.ts +6 -3
  48. package/lib/types/providers/local/provider.d.ts +83 -23
  49. package/lib/types/providers/ssh/provider.d.ts +24 -57
  50. package/lib/types/remote/environment.d.ts +2 -0
  51. package/lib/types/remote/remote-fs.d.ts +12 -1
  52. package/lib/types/remote/remote-process.d.ts +22 -1
  53. package/lib/types/remote/remote-subprocess.d.ts +13 -1
  54. package/lib/types/remote/remote-terminal.d.ts +4 -0
  55. package/lib/types/remote-search.d.ts +3 -3
  56. package/lib/types/routes.d.ts +36 -8
  57. package/lib/types/runtime/dsh-capabilities.d.ts +18 -0
  58. package/lib/types/runtime/workspace-core.d.ts +73 -38
  59. package/lib/types/runtime/workspace-migration.d.ts +113 -0
  60. package/lib/types/ssh/connection/lease.d.ts +23 -0
  61. package/lib/types/ssh/connection/manager.d.ts +359 -0
  62. package/lib/types/ssh/connection/pool.d.ts +8 -1
  63. package/lib/types/ssh/engine.d.ts +97 -267
  64. package/lib/types/ssh/known-hosts.d.ts +22 -5
  65. package/lib/types/ssh/local-transfer-policy.d.ts +20 -0
  66. package/lib/types/ssh/plugin.d.ts +10 -15
  67. package/lib/types/ssh/protocol.d.ts +18 -0
  68. package/lib/types/ssh/routes.d.ts +15 -7
  69. package/lib/types/ssh/sftp/service.d.ts +216 -0
  70. package/lib/types/ssh/store.d.ts +20 -8
  71. package/lib/types/ssh/terminal/service.d.ts +49 -0
  72. package/lib/types/ssh/tunnel/service.d.ts +26 -0
  73. package/lib/types/ssh/vault.d.ts +51 -4
  74. package/lib/types/subprocess.d.ts +13 -21
  75. package/lib/types/switch/switch-fs.d.ts +52 -2
  76. package/lib/types/switch/switch-subprocess.d.ts +32 -3
  77. package/lib/types/tools.d.ts +7 -6
  78. package/lib/types/workspace-tool-ops.d.ts +33 -0
  79. package/lib/types/workspace.d.ts +18 -0
  80. package/lib/vault-3gpWct2Q.js +559 -0
  81. package/lib/workspace.js +2 -0
  82. package/package.json +58 -25
  83. package/scripts/export-legacy-workspaces.mjs +136 -0
  84. package/src/backend.ts +155 -483
  85. package/src/base/capability.ts +4 -0
  86. package/src/base/ledger-router.ts +214 -65
  87. package/src/base/ledger.ts +175 -48
  88. package/src/base/model.ts +18 -8
  89. package/src/base/plugin.ts +15 -4
  90. package/src/base/registry.ts +49 -41
  91. package/src/base/router.ts +0 -8
  92. package/src/client/api.ts +8 -36
  93. package/src/client/connect-host.ts +199 -0
  94. package/src/client/directory-flow.tsx +91 -36
  95. package/src/client/index.ts +56 -53
  96. package/src/client/locales.ts +14 -0
  97. package/src/client/session-connect-gate.ts +163 -0
  98. package/src/client/ssh/api.ts +245 -108
  99. package/src/client/ssh/apply.ts +23 -24
  100. package/src/client/ssh/locales.ts +16 -2
  101. package/src/client/ssh/ops-tab.tsx +79 -0
  102. package/src/client/ssh/panel/ClusterTab.tsx +9 -20
  103. package/src/client/ssh/panel/ConnectionErrorDialog.tsx +33 -0
  104. package/src/client/ssh/panel/SessionSecretDialog.tsx +5 -1
  105. package/src/client/ssh/panel/SshPanel.tsx +81 -77
  106. package/src/client/ssh/panel/TerminalTab.tsx +18 -37
  107. package/src/client/ssh/panel/TransferTab.tsx +43 -33
  108. package/src/client/ssh/panel/TunnelsTab.tsx +9 -26
  109. package/src/client/ssh/panel/panel.module.css +90 -127
  110. package/src/client/ssh/session-target.ts +69 -0
  111. package/src/client/state.ts +9 -1
  112. package/src/client/workspace-badges.ts +99 -96
  113. package/src/client/workspace-panel-entry.tsx +91 -0
  114. package/src/client/workspace-panel.tsx +227 -0
  115. package/src/client/workspace.module.css +76 -2
  116. package/src/client-http.ts +118 -18
  117. package/src/core.ts +37 -47
  118. package/src/fs.ts +137 -85
  119. package/src/index.ts +535 -244
  120. package/src/ledger.ts +62 -416
  121. package/src/protocol.ts +0 -6
  122. package/src/providers/index.ts +8 -4
  123. package/src/providers/local/provider.ts +259 -87
  124. package/src/providers/ssh/provider.ts +158 -155
  125. package/src/remote/environment.ts +22 -1
  126. package/src/remote/remote-fs.ts +123 -34
  127. package/src/remote/remote-process.ts +265 -203
  128. package/src/remote/remote-subprocess.ts +61 -18
  129. package/src/remote/remote-terminal.ts +106 -21
  130. package/src/remote-search.ts +122 -26
  131. package/src/routes.ts +416 -395
  132. package/src/runtime/dsh-capabilities.ts +19 -0
  133. package/src/runtime/workspace-core.ts +171 -88
  134. package/src/runtime/workspace-migration.ts +472 -0
  135. package/src/ssh/connection/lease.ts +35 -0
  136. package/src/ssh/connection/manager.ts +1083 -0
  137. package/src/ssh/connection/pool.ts +341 -275
  138. package/src/ssh/engine.ts +269 -1477
  139. package/src/ssh/known-hosts.ts +42 -18
  140. package/src/ssh/local-transfer-policy.ts +83 -0
  141. package/src/ssh/plugin.ts +20 -19
  142. package/src/ssh/protocol.ts +22 -1
  143. package/src/ssh/routes.ts +419 -187
  144. package/src/ssh/sftp/service.ts +967 -0
  145. package/src/ssh/store.ts +169 -72
  146. package/src/ssh/terminal/service.ts +177 -0
  147. package/src/ssh/tools.ts +42 -20
  148. package/src/ssh/tunnel/service.ts +217 -0
  149. package/src/ssh/vault.ts +245 -91
  150. package/src/subprocess.ts +88 -71
  151. package/src/switch/switch-fs.ts +141 -10
  152. package/src/switch/switch-subprocess.ts +80 -6
  153. package/src/tools.ts +217 -221
  154. package/src/workspace-tool-ops.ts +101 -0
  155. package/src/workspace.ts +52 -0
  156. package/lib/environment-BL1jddfB.js +0 -449
  157. package/lib/switch-fs-CAJpFY9C.js +0 -193
  158. package/lib/switch-fs-RrZtG2gv.js +0 -210
  159. package/lib/types/client/manager-button.d.ts +0 -32
  160. package/lib/types/client/ssh/mount.d.ts +0 -13
  161. package/lib/types/client/ssh/panel/HostsTab.d.ts +0 -10
  162. package/lib/types/client/ssh/panel/controller.d.ts +0 -23
  163. package/lib/types/client/ssh/sidebar-entry.d.ts +0 -25
  164. package/lib/types/client/workspace-gate.d.ts +0 -15
  165. package/lib/types/remote-runner.d.ts +0 -83
  166. package/lib/types/seam-state.d.ts +0 -69
  167. package/src/client/manager-button.tsx +0 -269
  168. package/src/client/ssh/mount.tsx +0 -83
  169. package/src/client/ssh/panel/HostsTab.tsx +0 -236
  170. package/src/client/ssh/panel/controller.ts +0 -48
  171. package/src/client/ssh/sidebar-entry.ts +0 -123
  172. package/src/client/workspace-gate.ts +0 -225
  173. package/src/remote-runner.ts +0 -201
  174. 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