mirror of
https://github.com/stablyai/orca.git
synced 2026-11-02 10:08:13 +00:00
* wip: connect fails for hosts the managed server can't serve; remove relay session, deploy and relay UI * wip: delete relay-only client modules left unreachable, prune their tests and gates * wip: format * wip: exhaustive unserved message * wip: drop relay attestation test for the deleted ownership proof * fix: prune stale baselines; type the test transport stub * fix(ssh): census proves leftover relay leases; name the remedy for older terminals; translate every connect-failure surface * test(ssh): leftover leases defer to the host census * test(e2e): remove relay-path SSH e2e specs and their CI lanes * test(e2e): drop the auto-convert case that built its relay era through a live relay * test(e2e): mark relay-era browser and catalog journeys fixme until they seed a profile * test(e2e): seed relay-era browser and catalog journeys from a saved profile The three journeys built their old-version state through a connect without an orcad template, which now refuses the host. They now start from a seeded relay-era profile, so the first connect converts the host as an upgrading user's would. * chore(ci): drop shard timings for the deleted relay tests * test(e2e): carry a relay-era login cookie across the browser conversion; own every launch * test(ci): drop the deleted SSH layout oracle from its route * test(e2e): open the retained browser after conversion, retrying a pre-connect card * test(e2e): bind the relay-era browser partition before seeding its cookie A relay-era build always recorded the partition binding before a page stored data in it. Seeding the cookie into an unbound partition made the binding store refuse it (browser_route_partition_binding_store_invalid), so the retained tab showed the generic routing card and Retry could never clear it. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: Claude <noreply@anthropic.com>
116 lines
4.4 KiB
TypeScript
116 lines
4.4 KiB
TypeScript
/** The connect path's managed-server step: decide the host's server, and publish a managed connect. */
|
|
import { getAppEnvironment } from '../../shared/app-environment'
|
|
import type {
|
|
SshConnectionState,
|
|
SshManagedServerServingNote,
|
|
SshManagedServerUpdateNote,
|
|
SshTarget
|
|
} from '../../shared/ssh-types'
|
|
import type { HostServerOnConnectResult } from '../ssh/ssh-host-server-on-connect'
|
|
import { unservedHostServerMessage, unservedServerStatus } from '../ssh/ssh-host-unserved-status'
|
|
import {
|
|
clearSshHostServerStatus,
|
|
getSshHostServerStatus,
|
|
setSshHostServerStatus
|
|
} from '../ssh/ssh-host-server-status'
|
|
import { connectionManager, getCurrentMainWindow } from './ssh-ipc-context'
|
|
import { restoreManagedHostPortForwards } from './ssh-managed-port-forward-restore'
|
|
import { broadcastSshState, getPublicSshState } from './ssh-renderer-broadcast'
|
|
import { isAuthError } from '../ssh/ssh-connection-utils'
|
|
|
|
export async function decideHostServer(target: SshTarget): Promise<HostServerOnConnectResult> {
|
|
// Why lazy: the managed-server graph (deploy, migration, tunnel) loads only when a host connects.
|
|
const [{ resolveHostServerOnConnect }, { hostServerOnConnectDeps }] = await Promise.all([
|
|
import('../ssh/ssh-host-server-on-connect'),
|
|
import('./ssh-host-server-on-connect-wiring')
|
|
])
|
|
return await resolveHostServerOnConnect(
|
|
target,
|
|
hostServerOnConnectDeps(getAppEnvironment().getPath('userData'))
|
|
)
|
|
}
|
|
|
|
/** Rechecks a host another desktop's update held during this connect, until the fence clears. */
|
|
export function recheckWhenManagedFenceClears(target: SshTarget, environmentId: string): void {
|
|
void Promise.all([
|
|
import('../ssh/managed-server-fence-recheck'),
|
|
import('./ssh-host-server-on-connect-wiring')
|
|
]).then(([{ recheckFencedManagedServer, scheduleManagedServerFenceRecheck }, wiring]) => {
|
|
const deps = wiring.hostServerOnConnectDeps(getAppEnvironment().getPath('userData'))
|
|
scheduleManagedServerFenceRecheck(target.id, {
|
|
stillCurrent: () => {
|
|
const status = getSshHostServerStatus(target.id)
|
|
return (
|
|
connectionManager?.getState(target.id)?.status === 'connected' &&
|
|
status?.kind === 'managed' &&
|
|
status.environmentId === environmentId
|
|
)
|
|
},
|
|
recheck: () => recheckFencedManagedServer(target, environmentId, deps),
|
|
publish: (result) =>
|
|
publishManagedServerConnect(target.id, environmentId, result.update, result.serving)
|
|
})
|
|
})
|
|
}
|
|
|
|
/**
|
|
* A host only its managed server reaches failed to set up: leave 'connecting' and the 'setting
|
|
* up' status for the error, as a failed transport connect does.
|
|
*/
|
|
export function publishHostServerDecisionFailure(targetId: string, error: unknown): void {
|
|
const failure = error instanceof Error ? error : new Error(String(error))
|
|
clearSshHostServerStatus(targetId)
|
|
broadcastSshState(getCurrentMainWindow, targetId, {
|
|
targetId,
|
|
status: isAuthError(failure) ? 'auth-failed' : 'error',
|
|
error: failure.message,
|
|
reconnectAttempt: 0
|
|
})
|
|
}
|
|
|
|
export function publishManagedServerConnect(
|
|
targetId: string,
|
|
environmentId: string,
|
|
update?: SshManagedServerUpdateNote,
|
|
serving?: SshManagedServerServingNote
|
|
): SshConnectionState {
|
|
const managedServer = {
|
|
kind: 'managed' as const,
|
|
environmentId,
|
|
...(update ? { update } : {}),
|
|
...(serving ? { serving } : {})
|
|
}
|
|
setSshHostServerStatus(targetId, managedServer)
|
|
void restoreManagedHostPortForwards(targetId)
|
|
const state: SshConnectionState = {
|
|
...(connectionManager!.getState(targetId) ?? { targetId, reconnectAttempt: 0 }),
|
|
targetId,
|
|
status: 'connected',
|
|
error: null,
|
|
managedServer
|
|
}
|
|
broadcastSshState(getCurrentMainWindow, targetId, state)
|
|
return getPublicSshState(targetId) ?? state
|
|
}
|
|
|
|
/**
|
|
* A host the managed server doesn't serve: publish why and fail the connect. There is no fallback;
|
|
* the renderer translates the status, and this message is only its untranslated twin.
|
|
*/
|
|
export function publishUnservedHostServer(
|
|
target: SshTarget,
|
|
decision: Extract<HostServerOnConnectResult, { route: 'relay' }>
|
|
): Error {
|
|
const managedServer = unservedServerStatus(decision)
|
|
setSshHostServerStatus(target.id, managedServer)
|
|
const failure = new Error(unservedHostServerMessage(decision))
|
|
broadcastSshState(getCurrentMainWindow, target.id, {
|
|
targetId: target.id,
|
|
status: 'error',
|
|
error: failure.message,
|
|
reconnectAttempt: 0,
|
|
managedServer
|
|
})
|
|
return failure
|
|
}
|