Files
orca/src/main/ssh/system-ssh-windows-write-capabilities.ts
T
OrcaWinandm4air 4085e1cf60 fix(memory): release stale session registries (#21734)
* fix(memory): bound session and lifecycle registries

* fix(memory): bound transient filesystem registries

* fix(memory): cap path and locale caches

* fix(memory): bound runtime recovery registries

* fix(memory): bound host mirror gap verdicts

* fix(memory): bound shell startup env cache

* fix(memory): bound gitlab host context cache

* fix(memory): release removed ssh generations

* fix(memory): expire cloud refresh replay guards

* fix(memory): release retired plugin generations

* fix(memory): bound plugin log key retention

* fix(memory): bound automation authority generations

* fix(memory): bound native chat enrichment cache

* fix(memory): bound web session tracking generations

* fix(memory): bound codex credential absence paths

* fix(memory): bound WSL canonical path cache

* fix(memory): bound sparse checkout cache

* fix(memory): bound shared directory cache

* fix(memory): bound advertised URL scan snapshots

* fix(memory): bound automation manager cache

* fix(memory): bound web session reorder intents

* fix(memory): bound web session focus intents

* fix(memory): bound web session handoffs

* fix(memory): bound automation dispatch tokens

* fix(memory): bound host mirror waiters

* fix(memory): bound retained session activity

* fix(memory): bound retained session activity

* fix(memory): bound web session close intents

* fix(memory): bound cloud session cache

* fix(memory): bound WSL home cache

* fix(memory): bound SSH capability cache

* fix(memory): bound trust grant cooldowns

* fix(memory): bound WSL auth drain state

* fix(memory): bound Linear workspace credential cache

* fix(memory): bound local Git capability cache

* fix(memory): bound WSL Git environment cache

* fix(memory): bound WSL Git environment cache

* fix(memory): bound WSL preflight cache

* fix(memory): keep hot cache entries warm

* fix(memory): preserve generation fences across eviction

* fix(memory): close remaining eviction fences

* fix(memory): align evicted upstream generations

* fix(memory): trim successful capability probes

* fix(auth): retain expired refresh replay evidence

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
2026-09-20 14:41:50 -07:00

69 lines
2.6 KiB
TypeScript

import type { SshTarget } from '../../shared/ssh-types'
import { CapabilityProbeCache } from '../../shared/capability-probe-cache'
/**
* Whether a Windows host can take a file write over the sftp subsystem, and whether it has a
* PowerShell 7 to fall back to. Both are host facts, so they are cached per execution host rather
* than per transfer — a hardened host with `Subsystem sftp` removed must not be re-probed on every
* file of a multi-file upload.
*/
export type WindowsRemoteWriteCapability = 'sftp-subsystem' | 'pwsh'
// Why re-probe at all: an admin can enable the subsystem, or install PowerShell 7, without the
// user restarting Orca. Long enough that a hardened host costs one failed probe per half hour.
export const WINDOWS_WRITE_CAPABILITY_RETRY_INTERVAL_MS = 30 * 60_000
const MAX_WINDOWS_WRITE_CAPABILITY_HOSTS = 256
const capabilitiesByExecutionHost = new Map<
string,
CapabilityProbeCache<WindowsRemoteWriteCapability>
>()
function rememberCapabilityCache(
key: string,
cache: CapabilityProbeCache<WindowsRemoteWriteCapability>
): CapabilityProbeCache<WindowsRemoteWriteCapability> {
capabilitiesByExecutionHost.delete(key)
capabilitiesByExecutionHost.set(key, cache)
while (capabilitiesByExecutionHost.size > MAX_WINDOWS_WRITE_CAPABILITY_HOSTS) {
const oldest = capabilitiesByExecutionHost.keys().next().value
if (oldest === undefined) {
break
}
capabilitiesByExecutionHost.delete(oldest)
}
return cache
}
/**
* Keyed by the endpoint that executes, not by target id: two Orca targets pointing at one host
* describe the same sshd, and a target re-created under a new id has not changed what that host
* supports. A config alias is its own key because ssh_config, not Orca, resolves where it lands.
*/
export function getWindowsRemoteWriteExecutionHostKey(target: SshTarget): string {
if (target.configHost) {
return `config:${target.configHost}`
}
const port = target.port ?? 22
return target.username
? `host:${target.username}@${target.host}:${port}`
: `host:${target.host}:${port}`
}
export function getWindowsRemoteWriteCapabilities(
target: SshTarget
): CapabilityProbeCache<WindowsRemoteWriteCapability> {
const key = getWindowsRemoteWriteExecutionHostKey(target)
let cache = capabilitiesByExecutionHost.get(key)
if (!cache) {
cache = new CapabilityProbeCache<WindowsRemoteWriteCapability>(
WINDOWS_WRITE_CAPABILITY_RETRY_INTERVAL_MS
)
}
return rememberCapabilityCache(key, cache)
}
export function clearWindowsRemoteWriteCapabilitiesForTests(): void {
capabilitiesByExecutionHost.clear()
}