fix(desktop): do not treat deferred local enumeration as a failure
'connect-on-demand' means local roster enumeration was intentionally skipped to avoid spawning a local backend on a remote-only workspace, not that it failed. The plugin-profile-routes IPC handler passed Boolean(error) straight through, so that deferral was treated as a genuine failure and Bot Mode re-synthesized cached local profile rows even though local was never dialed. Fixes #94648
This commit is contained in:
@@ -244,6 +244,7 @@ import { createParentStartMarkerResolver, parentWatchdogEnv } from './parent-pro
|
||||
import { registerPetOverlayIpc } from './pet-overlay-ipc'
|
||||
import {
|
||||
buildRegistryProfileRoutes,
|
||||
isLocalEnumerationFailure,
|
||||
localRouteFallbackProfiles,
|
||||
registryGatewayWsUrl,
|
||||
undialedSshRouteSeeds
|
||||
@@ -13367,7 +13368,12 @@ ipcMain.handle('hermes:plugin-profile-routes', async (_event, rawProfileNames) =
|
||||
: undefined
|
||||
|
||||
const localFallbackProfiles = localSource
|
||||
? localRouteFallbackProfiles(agents, localSource.id, fallbackProfileNames, Boolean(localEnumeration?.error))
|
||||
? localRouteFallbackProfiles(
|
||||
agents,
|
||||
localSource.id,
|
||||
fallbackProfileNames,
|
||||
isLocalEnumerationFailure(localEnumeration?.error)
|
||||
)
|
||||
: []
|
||||
|
||||
if (localSource && localFallbackProfiles.length > 0) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
buildOpaqueProfileRoutes,
|
||||
buildRegistryProfileRoutes,
|
||||
isLocalEnumerationFailure,
|
||||
localRouteFallbackProfiles,
|
||||
type ProfileRouteConfig,
|
||||
registryGatewayWsUrl,
|
||||
@@ -253,6 +254,20 @@ describe('buildRegistryProfileRoutes', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('isLocalEnumerationFailure', () => {
|
||||
it('does not treat an intentionally deferred local enumeration as a failure', () => {
|
||||
expect(isLocalEnumerationFailure('connect-on-demand')).toBe(false)
|
||||
})
|
||||
|
||||
it('treats any other enumeration error as a failure', () => {
|
||||
expect(isLocalEnumerationFailure('ECONNREFUSED')).toBe(true)
|
||||
})
|
||||
|
||||
it('treats a missing error as no failure', () => {
|
||||
expect(isLocalEnumerationFailure(undefined)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('localRouteFallbackProfiles', () => {
|
||||
it('restores failed local profiles when another source returned agents', () => {
|
||||
const agents = [{ connectionId: 'cloud-prod', profile: 'default' }]
|
||||
|
||||
@@ -51,6 +51,13 @@ interface BuildOpaqueProfileRoutesOptions {
|
||||
resolveSsh: (config: ProfileRouteConfig) => Promise<EffectiveSshRoute>
|
||||
}
|
||||
|
||||
/** A 'connect-on-demand' local enumeration was intentionally deferred, not
|
||||
* failed — it must not be treated as a failure or Bot Mode will synthesize
|
||||
* cached local rows on remote-only workspaces where local was never dialed. */
|
||||
export function isLocalEnumerationFailure(error?: string): boolean {
|
||||
return Boolean(error) && error !== 'connect-on-demand'
|
||||
}
|
||||
|
||||
/** Return cached local profile names only when the local roster read failed. */
|
||||
export function localRouteFallbackProfiles(
|
||||
agents: RegistryProfileRouteAgent[],
|
||||
|
||||
Reference in New Issue
Block a user