(
config: SSHConnectionConfig,
timeoutMsOrOptions: number | AcquireConnectionOptions = DEFAULT_PROBE_TIMEOUT_MS
)
| 194 | options?: AcquireConnectionOptions |
| 195 | ): Promise<void>; |
| 196 | async acquireConnection( |
| 197 | config: SSHConnectionConfig, |
| 198 | timeoutMsOrOptions: number | AcquireConnectionOptions = DEFAULT_PROBE_TIMEOUT_MS |
| 199 | ): Promise<void> { |
| 200 | const options: AcquireConnectionOptions = |
| 201 | typeof timeoutMsOrOptions === "number" |
| 202 | ? { timeoutMs: timeoutMsOrOptions } |
| 203 | : (timeoutMsOrOptions ?? {}); |
| 204 | |
| 205 | const timeoutMs = options.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS; |
| 206 | const sleep = options.sleep ?? sleepWithAbort; |
| 207 | |
| 208 | const maxWaitMs = options.maxWaitMs ?? DEFAULT_SSH_MAX_WAIT_MS; |
| 209 | const shouldWait = maxWaitMs > 0; |
| 210 | |
| 211 | const key = makeConnectionKey(config); |
| 212 | const requestedControlPath = options.controlPath ?? getControlPath(config); |
| 213 | const startTime = Date.now(); |
| 214 | const getRemainingWaitBudgetMs = (): number => |
| 215 | Math.max(0, maxWaitMs - (Date.now() - startTime)); |
| 216 | const createWaitBudgetExceededError = (lastError?: string): Error => |
| 217 | new Error( |
| 218 | `SSH connection to ${config.host} did not become healthy within ${maxWaitMs}ms. ` + |
| 219 | `Last error: ${lastError ?? "unknown"}` |
| 220 | ); |
| 221 | |
| 222 | while (true) { |
| 223 | if (options.abortSignal?.aborted) { |
| 224 | throw new Error(SSH_OPERATION_ABORTED_ERROR); |
| 225 | } |
| 226 | |
| 227 | const health = this.health.get(key); |
| 228 | |
| 229 | // If in backoff: either fail fast or wait (bounded). |
| 230 | if (health?.backoffUntil && health.backoffUntil > new Date()) { |
| 231 | const remainingMs = health.backoffUntil.getTime() - Date.now(); |
| 232 | const remainingSecs = Math.ceil(remainingMs / 1000); |
| 233 | |
| 234 | if (!shouldWait) { |
| 235 | throw new Error( |
| 236 | `SSH connection to ${config.host} is in backoff for ${remainingSecs}s. ` + |
| 237 | `Last error: ${health.lastError ?? "unknown"}` |
| 238 | ); |
| 239 | } |
| 240 | |
| 241 | const budgetMs = getRemainingWaitBudgetMs(); |
| 242 | if (budgetMs <= 0) { |
| 243 | throw createWaitBudgetExceededError(health.lastError); |
| 244 | } |
| 245 | |
| 246 | const waitMs = Math.min(remainingMs, budgetMs); |
| 247 | options.onWait?.(waitMs); |
| 248 | await sleep(waitMs, options.abortSignal); |
| 249 | continue; |
| 250 | } |
| 251 | |
| 252 | // Return immediately if known healthy and not stale. |
| 253 | if (health?.status === "healthy") { |
nothing calls this directly
no test coverage detected