mirror of
https://github.com/stablyai/orca.git
synced 2026-09-29 16:02:50 +00:00
71 lines
2.5 KiB
TypeScript
71 lines
2.5 KiB
TypeScript
import { getSshFilesystemProvider } from './providers/ssh-filesystem-dispatch'
|
|
import { getSshGitProvider } from './providers/ssh-git-dispatch'
|
|
import { connectRegisteredSshTarget, getSshConnectionStore } from './ipc/ssh'
|
|
import {
|
|
disconnectRegisteredSshTarget,
|
|
removeRegisteredSshTarget
|
|
} from './ipc/ssh-session-teardown'
|
|
import type { EphemeralVmRecipeConnection } from '../shared/ephemeral-vm-recipes'
|
|
import type { SshTarget } from '../shared/ssh-types'
|
|
|
|
const SSH_PROVIDER_READY_TIMEOUT_MS = 10_000
|
|
const SSH_PROVIDER_READY_INTERVAL_MS = 100
|
|
|
|
export type RuntimeOwnedSshConnectionResult = {
|
|
targetId: string
|
|
target: SshTarget
|
|
}
|
|
|
|
export async function connectRuntimeOwnedSshTarget(args: {
|
|
runtimeId: string
|
|
connection: Extract<EphemeralVmRecipeConnection, { type: 'ssh' }>
|
|
signal?: AbortSignal
|
|
}): Promise<RuntimeOwnedSshConnectionResult> {
|
|
const store = getSshConnectionStore()
|
|
if (!store) {
|
|
throw new Error('SSH handlers are not registered.')
|
|
}
|
|
const target = store.upsertRuntimeOwnedTarget(args.runtimeId, args.connection.target)
|
|
try {
|
|
const state = await connectRegisteredSshTarget(target.id)
|
|
if (state.status !== 'connected') {
|
|
throw new Error(state.error || `SSH target did not connect: ${state.status}`)
|
|
}
|
|
await waitForRuntimeSshProviders(target.id, args.signal)
|
|
} catch (error) {
|
|
// The target is persisted at upsert, so a failed connect/provider-wait would
|
|
// orphan it; remove it (idempotent) before rethrowing so cleanup is complete.
|
|
await removeRuntimeOwnedSshTarget(target.id).catch(() => undefined)
|
|
throw error
|
|
}
|
|
return { targetId: target.id, target }
|
|
}
|
|
|
|
export async function disconnectRuntimeOwnedSshTarget(targetId: string | undefined): Promise<void> {
|
|
if (!targetId) {
|
|
return
|
|
}
|
|
await disconnectRegisteredSshTarget(targetId)
|
|
}
|
|
|
|
export async function removeRuntimeOwnedSshTarget(targetId: string | undefined): Promise<void> {
|
|
if (!targetId) {
|
|
return
|
|
}
|
|
await removeRegisteredSshTarget(targetId)
|
|
}
|
|
|
|
async function waitForRuntimeSshProviders(targetId: string, signal?: AbortSignal): Promise<void> {
|
|
const startedAt = Date.now()
|
|
while (Date.now() - startedAt < SSH_PROVIDER_READY_TIMEOUT_MS) {
|
|
if (signal?.aborted) {
|
|
throw new Error(`SSH provider wait aborted for target "${targetId}".`)
|
|
}
|
|
if (getSshGitProvider(targetId) && getSshFilesystemProvider(targetId)) {
|
|
return
|
|
}
|
|
await new Promise((resolve) => setTimeout(resolve, SSH_PROVIDER_READY_INTERVAL_MS))
|
|
}
|
|
throw new Error(`SSH relay providers were not ready for target "${targetId}".`)
|
|
}
|