Files
orca/src/main/ssh/ssh-plain-ssh-mode.ts
T
dfdcfcf61f feat(ssh): plain SSH terminals and SFTP browsing when no Orca runtime can run (#24147)
* feat(ssh): connect in plain SSH mode when no Orca runtime can run on the host

Runtime ladder rung D (design D6): instead of failing the connect, register an
ssh2 shell-channel PTY provider and an SFTP-only filesystem provider and publish
the classified reason on the SSH connection state.

* fix(ssh): harden plain SSH mode against stale reconnects, host sleep and tilde cwd

- Only the current connect or reconnect attempt may enter plain SSH mode; a superseded
  reconnect whose ladder ends at rung D no longer registers a second provider set.
- Host-sleep resume probes a plain session over SFTP instead of always reconnecting,
  which ended every open plain shell.
- A home-relative cwd keeps its tilde outside the quotes so the shell expands it.
- The SFTP provider implements folder download, which the connect state advertises.

* docs(ssh): rung D now means plain SSH mode, not a failed connect

---------

Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-01 03:24:26 -07:00

43 lines
1.6 KiB
TypeScript

/**
* Design D6 rung D: no Orca runtime runs on the host, so the target connects with plain SSH
* terminals and SFTP browsing only. This module owns the per-target mode record and the
* wording every relay-only feature uses when it refuses.
*/
import type { SshPlainSshMode } from '../../shared/ssh-types'
import type { RemoteRuntimeUnavailableError } from './ssh-relay-runtime-resolution'
const PLAIN_SSH_SUFFIX =
' Connected with plain SSH terminals and SFTP file browsing; these terminals end when the ' +
'connection drops and are not restored on reconnect.'
const plainModes = new Map<string, SshPlainSshMode>()
export function plainSshModeFromRuntimeUnavailable(
error: RemoteRuntimeUnavailableError
): SshPlainSshMode {
return { reason: error.reason, message: `${error.message}${PLAIN_SSH_SUFFIX}` }
}
export function setSshPlainSshMode(targetId: string, mode: SshPlainSshMode): void {
plainModes.set(targetId, mode)
}
export function clearSshPlainSshMode(targetId: string): void {
plainModes.delete(targetId)
}
export function getSshPlainSshMode(targetId: string): SshPlainSshMode | undefined {
return plainModes.get(targetId)
}
/** Refusal for an operation only the Orca remote server can perform. */
export class PlainSshUnsupportedError extends Error {
readonly code = 'plain_ssh_unsupported'
constructor(feature: string, mode: SshPlainSshMode | undefined) {
const reason = mode ? ` (${mode.reason})` : ''
super(`${feature} needs the Orca remote server, which is not running on this host${reason}.`)
this.name = 'PlainSshUnsupportedError'
}
}