Files
orca/src/main/ssh/ssh-remote-platform-detection.ts
T
98d02bca47 fix: support windows ssh hosts (#5004)
* feat: add windows ssh relay base support

* feat: support windows ssh relay runtime services

* fix: default windows ssh pty cwd to user profile

* fix: support windows hosts over system ssh

* fix: preserve degraded windows relay native deps

* fix: gate windows shell args by relay platform

* fix: preserve windows relay fallback pipes

* test: align windows native deps relay fixture

* fix: build valid windows install lock command

* fix: address windows SSH relay review findings

Resolve correctness, efficiency, and reuse issues found reviewing the
Windows SSH native-host support:

- GC liveness on Windows now probes the actual named pipe (via node
  net.connect against markers + deterministic candidates) instead of
  substring-matching Win32_Process command lines, which could remove a
  live relay dir. Reports ALIVE conservatively only when there is no
  liveness signal at all (no markers and no seed pipes).
- Resolve the remote node path once per deploy and thread it through
  install/repair/launch instead of re-resolving 3-7x.
- Replace the 200ms node -e poll loop with a single long-lived remote
  wait process during Windows relay startup.
- Skip the no-op executable command on Windows in uploadRelay.
- Make the Windows fallback pipe name deterministic and recoverable
  (drop the global counter), with an extra reconnect attempt.
- Normalize the prepended node bin dir to backslashes on Windows PATH.
- Batch the system-SSH Windows directory upload into a single streamed
  JSON package instead of one ssh process per file.
- Extract relay endpoint/marker helpers into ssh-relay-endpoints.ts and
  consolidate the PowerShell EncodedCommand encoding into the shared
  powershell-command-encoding module.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Support cancellation and timeouts in Windows port scanning

- Propagate the request AbortSignal and a 5-second timeout to both
  PowerShell and netstat child processes during Windows port scanning.
- Avoid spawning the netstat fallback process if the port scan has
  already been aborted.
- Wrap the .NET OSArchitecture check in a try/catch block during SSH
  Windows platform detection to robustly fall back to environment
  variables if needed.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-06-09 01:17:34 -07:00

49 lines
1.8 KiB
TypeScript

import type { SshConnection } from './ssh-connection'
import { parseUnameToRelayPlatform, type RelayPlatform } from './relay-protocol'
import { execCommand } from './ssh-relay-deploy-helpers'
import { getRemoteHostPlatform, type RemoteHostPlatform } from './ssh-remote-platform'
import { powerShellCommand } from './ssh-remote-powershell'
export async function detectRemoteHostPlatform(
conn: SshConnection
): Promise<RemoteHostPlatform | null> {
const unamePlatform = await detectUnamePlatform(conn)
if (unamePlatform) {
return getRemoteHostPlatform(unamePlatform)
}
const windowsPlatform = await detectWindowsPlatform(conn)
return windowsPlatform ? getRemoteHostPlatform(windowsPlatform) : null
}
async function detectUnamePlatform(conn: SshConnection): Promise<RelayPlatform | null> {
try {
const output = await execCommand(conn, 'uname -sm')
const parts = output.trim().split(/\s+/)
if (parts.length < 2) {
return null
}
return parseUnameToRelayPlatform(parts[0], parts[1])
} catch {
return null
}
}
async function detectWindowsPlatform(conn: SshConnection): Promise<RelayPlatform | null> {
try {
const script = [
'$arch = $env:PROCESSOR_ARCHITECTURE',
'try { $runtimeArch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString(); if ($runtimeArch) { $arch = $runtimeArch } } catch {}',
'if (-not $arch) { $arch = $env:PROCESSOR_ARCHITECTURE }',
'Write-Output ("Windows " + $arch)'
].join('; ')
const output = await execCommand(conn, powerShellCommand(script), { wrapCommand: false })
const parts = output.trim().split(/\s+/)
if (parts.length < 2 || parts[0].toLowerCase() !== 'windows') {
return null
}
return parseUnameToRelayPlatform('Windows', parts[1])
} catch {
return null
}
}