diff --git a/apps/desktop/electron/main.cjs b/apps/desktop/electron/main.cjs index 922b7b2b8db..84c8c424b36 100644 --- a/apps/desktop/electron/main.cjs +++ b/apps/desktop/electron/main.cjs @@ -6619,6 +6619,17 @@ ipcMain.handle('hermes:cloud:status', async () => ({ portalBaseUrl: resolvePortalBaseUrl(), signedIn: await hasLivePortalSession() })) + +// Whether the Hermes Cloud gateway selector is enabled — gated behind a BETA +// env flag while the feature is in beta. Truthy values: '1', 'true', 'yes', +// 'on' (case-insensitive). Absent, empty, 'false', '0', etc. → disabled, so the +// renderer hides the Cloud ModeCard entirely. +function betaFeaturesEnabled() { + const raw = String(process.env.BETA ?? '').trim().toLowerCase() + return raw === '1' || raw === 'true' || raw === 'yes' || raw === 'on' +} + +ipcMain.handle('hermes:cloud:beta-enabled', async () => betaFeaturesEnabled()) ipcMain.handle('hermes:cloud:login', async () => { await openPortalLoginWindow() return { ok: true, signedIn: await hasLivePortalSession() } diff --git a/apps/desktop/electron/preload.cjs b/apps/desktop/electron/preload.cjs index a4bfeddd30a..17b4da9995a 100644 --- a/apps/desktop/electron/preload.cjs +++ b/apps/desktop/electron/preload.cjs @@ -45,6 +45,7 @@ contextBridge.exposeInMainWorld('hermesDesktop', { // (cloud-auto-discovery Phase 3). cloud: { status: () => ipcRenderer.invoke('hermes:cloud:status'), + betaEnabled: () => ipcRenderer.invoke('hermes:cloud:beta-enabled'), login: () => ipcRenderer.invoke('hermes:cloud:login'), logout: () => ipcRenderer.invoke('hermes:cloud:logout'), discover: org => ipcRenderer.invoke('hermes:cloud:discover', org), diff --git a/apps/desktop/src/app/settings/gateway-settings.tsx b/apps/desktop/src/app/settings/gateway-settings.tsx index 44b36342568..754987a7732 100644 --- a/apps/desktop/src/app/settings/gateway-settings.tsx +++ b/apps/desktop/src/app/settings/gateway-settings.tsx @@ -129,6 +129,33 @@ export function GatewaySettings() { // not yet chosen / single-org user). const [cloudOrgs, setCloudOrgs] = useState([]) const [cloudOrg, setCloudOrg] = useState(null) + // Hermes Cloud is beta-gated: the selector ModeCard only appears when the main + // process reports the BETA env flag is enabled. Default hidden until the async + // check resolves, so it never flashes in for non-beta users. + const [cloudBetaEnabled, setCloudBetaEnabled] = useState(false) + useEffect(() => { + const desktop = window.hermesDesktop + + if (!desktop?.cloud?.betaEnabled) { + return + } + + let cancelled = false + desktop.cloud + .betaEnabled() + .then(enabled => { + if (!cancelled) { + setCloudBetaEnabled(Boolean(enabled)) + } + }) + .catch(() => { + if (!cancelled) { + setCloudBetaEnabled(false) + } + }) + + return () => void (cancelled = true) + }, []) // Connection scope: null = the global/default connection (the original // behavior); a profile name = that profile's per-profile remote override, so @@ -722,7 +749,7 @@ export function GatewaySettings() { ) : null} -
+
setState(current => ({ ...current, mode: 'local' }))} title={g.localTitle} /> - setState(current => ({ ...current, mode: 'cloud' }))} - title={g.cloudTitle} - /> + {cloudBetaEnabled ? ( + setState(current => ({ ...current, mode: 'cloud' }))} + title={g.cloudTitle} + /> + ) : null} Promise + // Whether the BETA env flag enables the Hermes Cloud gateway selector. + betaEnabled: () => Promise login: () => Promise logout: () => Promise discover: (org?: string) => Promise