fix(desktop): align SSH terminal routing precedence

This commit is contained in:
Mabolla
2026-08-20 23:01:02 +03:00
committed by Teknium
parent a70e7b0ca7
commit d77114d0d0
5 changed files with 146 additions and 76 deletions
@@ -1,32 +0,0 @@
import assert from 'node:assert/strict'
import { test } from 'vitest'
import { normalizeRegistry, REGISTRY_VERSION } from './connection-registry'
import { activeRegistrySshScope } from './active-ssh-route'
function registry(lastUsed: string) {
return normalizeRegistry({
version: REGISTRY_VERSION,
primary: 'local',
lastUsed,
connections: [
{ id: 'local', kind: 'local', label: 'This device' },
{ id: 'remote', kind: 'remote', label: 'Remote', url: 'https://gateway.test' },
{ id: 'ssh-box', kind: 'ssh', label: 'SSH box', host: 'box.test', user: 'hermes' }
]
})
}
test('returns the v2 SSH pool scope for the last-used registry source', () => {
assert.equal(activeRegistrySshScope(registry('ssh-box'), 'default'), 'conn:ssh-box::default')
})
test('keeps the active profile in the v2 SSH pool scope', () => {
assert.equal(activeRegistrySshScope(registry('ssh-box'), 'worker'), 'conn:ssh-box::worker')
})
test('returns null for local and URL-backed registry sources', () => {
assert.equal(activeRegistrySshScope(registry('local'), 'default'), null)
assert.equal(activeRegistrySshScope(registry('remote'), 'default'), null)
})
-19
View File
@@ -1,19 +0,0 @@
import { backendScopeKey, type ConnectionRegistry } from './connection-registry'
/**
* Return the live SSH pool scope for the registry connection currently used by
* the Sessions workspace. Non-SSH sources deliberately return null so callers
* can preserve the legacy v1 routing fallback.
*/
export function activeRegistrySshScope(
registry: ConnectionRegistry,
profile: null | string | undefined
): null | string {
const connection = registry.connections.find(candidate => candidate.id === registry.lastUsed)
if (!connection || connection.kind !== 'ssh') {
return null
}
return backendScopeKey(connection.id, profile)
}
@@ -232,6 +232,77 @@ test('URL route fails closed for different token, headers, kind, or Cloud org',
}
})
test('profile remote wins over a registry-backed global SSH route', () => {
const route = resolveDesktopRemoteRoute({
config: {
mode: 'ssh',
remote: { mode: 'ssh', host: 'global-box.test', user: 'hermes' },
profiles: {
worker: { mode: 'remote', url: 'https://worker.test', authMode: 'token', token: tokenA }
}
},
profile: 'worker',
registry: registry('global-ssh', [
{ id: 'global-ssh', kind: 'ssh', label: 'Global SSH', host: 'global-box.test', user: 'hermes' },
{ id: 'worker-remote', kind: 'remote', label: 'Worker', url: 'https://worker.test', token: tokenA }
])
})
assert.equal(route?.kind, 'remote')
assert.equal(route?.source, 'profile')
assert.equal(route?.connectionId, 'worker-remote')
})
test('profile SSH wins over a different registry primary SSH route', () => {
const route = resolveDesktopRemoteRoute({
config: {
mode: 'ssh',
remote: { mode: 'ssh', host: 'global-box.test', user: 'hermes' },
profiles: {
worker: { mode: 'ssh', host: 'worker-box.test', user: 'hermes' }
}
},
profile: 'worker',
registry: registry('global-ssh', [
{ id: 'global-ssh', kind: 'ssh', label: 'Global SSH', host: 'global-box.test', user: 'hermes' },
{ id: 'worker-ssh', kind: 'ssh', label: 'Worker SSH', host: 'worker-box.test', user: 'hermes' }
])
})
assert.equal(route?.kind, 'ssh')
assert.equal(route?.source, 'profile')
assert.equal(route?.connectionId, 'worker-ssh')
})
test('environment remote wins over a registry-backed global SSH route', () => {
const route = resolveDesktopRemoteRoute({
config: {
mode: 'ssh',
remote: { mode: 'ssh', host: 'global-box.test', user: 'hermes' }
},
env: { url: 'https://env.test', token: 'env-token' },
registry: registry('global-ssh', [
{ id: 'global-ssh', kind: 'ssh', label: 'Global SSH', host: 'global-box.test', user: 'hermes' }
])
})
assert.equal(route?.kind, 'remote')
assert.equal(route?.source, 'env')
assert.equal(route?.connectionId, undefined)
})
test('local route does not inherit an unrelated registry SSH connection', () => {
const route = resolveDesktopRemoteRoute({
config: { mode: 'local' },
registry: registry('local', [
{ id: 'unused-ssh', kind: 'ssh', label: 'Unused SSH', host: 'box.test', user: 'hermes' }
])
})
assert.equal(route, null)
})
test('local config without overrides returns null', () => {
assert.equal(resolveDesktopRemoteRoute({ config: { mode: 'local' }, registry: registry('local', []) }), null)
})
+15 -25
View File
@@ -144,7 +144,6 @@ import {
updateEligibility,
upsertConnection
} from './connection-registry'
import { activeRegistrySshScope } from './active-ssh-route'
import { describeCrashReason, installCrashForensics } from './crash-forensics'
import { adoptServedDashboardToken } from './dashboard-token'
import { loadOrCreateInstallationId, sshOwnershipId } from './desktop-installation'
@@ -9521,35 +9520,26 @@ async function teardownSshConnection(profile) {
function activeSshTerminalTarget() {
const profile = primaryProfileKey()
const config = readDesktopConnectionConfig()
const registryScope = activeRegistrySshScope(readDesktopConnectionsRegistry(), profile)
const route = resolveDesktopRemoteRoute({
config,
env: {
token: process.env.HERMES_DESKTOP_REMOTE_TOKEN,
url: process.env.HERMES_DESKTOP_REMOTE_URL
},
profile,
registry: readDesktopConnectionsRegistry()
})
if (registryScope) {
const state = sshConnections.get(registryScope)
return state && state.ssh ? { ssh: state.ssh, scope: registryScope } : 'pending'
}
if (profileSshOverride(config, profile)) {
const scope = sshScopeKey(profile)
const state = sshConnections.get(scope)
return state && state.ssh ? { ssh: state.ssh, scope } : 'pending'
}
if (profileRemoteOverride(config, profile)) {
if (!route || route.kind !== 'ssh') {
return null
}
if (process.env.HERMES_DESKTOP_REMOTE_URL) {
return null
}
const scope = route.connectionId
? backendScopeKey(route.connectionId, profile)
: sshScopeKey(route.source === 'profile' ? profile : null)
const state = sshConnections.get(scope)
if (config.mode === 'ssh') {
const state = sshConnections.get('')
return state && state.ssh ? { ssh: state.ssh, scope: '' } : 'pending'
}
return null
return state && state.ssh ? { ssh: state.ssh, scope } : 'pending'
}
// Loopback reach for the browser pane. Scoped to the SSH connection that
@@ -0,0 +1,60 @@
import assert from 'node:assert/strict'
import { test } from 'vitest'
import { resolveTerminalConnection } from './connection-apply'
const ssh = {
host: 'registry-box.test',
user: 'hermes'
}
test('terminal start preserves the selected SSH target and scope', async () => {
const target = {
ssh,
scope: 'connection:registry-ssh:profile:worker'
}
const resolved = await resolveTerminalConnection(
() => target,
async () => {
throw new Error('backend fallback must not run for an active SSH target')
}
)
assert.equal(resolved, target)
assert.equal(resolved?.ssh, ssh)
assert.equal(resolved?.scope, 'connection:registry-ssh:profile:worker')
})
test('terminal start does not invent SSH when canonical routing selects local or remote HTTP', async () => {
let backendChecks = 0
const resolved = await resolveTerminalConnection(
() => null,
async () => {
backendChecks += 1
}
)
assert.equal(resolved, null)
assert.equal(backendChecks, 0)
})
test('terminal start re-reads the SSH target after backend startup', async () => {
const target = {
ssh,
scope: 'connection:registry-ssh'
}
let ready = false
const resolved = await resolveTerminalConnection(
() => (ready ? target : 'pending'),
async () => {
ready = true
}
)
assert.equal(resolved, target)
assert.equal(resolved?.scope, 'connection:registry-ssh')
})