mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
feat(desktop): gate the Hermes Cloud gateway selector behind a BETA env flag
The Hermes Cloud ModeCard in Settings → Gateway now only appears when the BETA
env var is truthy (1/true/yes/on, case-insensitive); absent/empty/false/0 hides
it. While the feature is in beta, non-beta users see only Local + Remote.
- main.cjs: betaFeaturesEnabled() reads process.env.BETA; exposed via new IPC
hermes☁️beta-enabled (the sandboxed renderer can't read process.env, and
runtime IPC means the same build honors BETA per-launch with no rebuild).
- preload.cjs / global.d.ts: cloud.betaEnabled() bridge + type.
- gateway-settings.tsx: fetch the flag on mount (default false so it never
flashes in for non-beta users), conditionally render the Cloud ModeCard, and
flip the grid sm:grid-cols-3 → sm:grid-cols-2 when hidden.
Gates the SELECTOR only — an already-saved cloud connection keeps working if
BETA is later turned off; only newly selecting cloud is hidden.
tsc + eslint clean; 57 node --test + 16 vitest pass; env parsing unit-checked
across 9 cases; gate verified in the packaged bundle.
cloud-auto-discovery beta gating.
This commit is contained in:
parent
7e1e9d62c4
commit
34d7d3fe2e
4 changed files with 52 additions and 9 deletions
|
|
@ -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() }
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -129,6 +129,33 @@ export function GatewaySettings() {
|
|||
// not yet chosen / single-org user).
|
||||
const [cloudOrgs, setCloudOrgs] = useState<DesktopCloudOrg[]>([])
|
||||
const [cloudOrg, setCloudOrg] = useState<null | string>(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() {
|
|||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="grid gap-3 sm:grid-cols-3">
|
||||
<div className={cn('grid gap-3', cloudBetaEnabled ? 'sm:grid-cols-3' : 'sm:grid-cols-2')}>
|
||||
<ModeCard
|
||||
active={state.mode === 'local'}
|
||||
description={g.localDesc}
|
||||
|
|
@ -731,14 +758,16 @@ export function GatewaySettings() {
|
|||
onSelect={() => setState(current => ({ ...current, mode: 'local' }))}
|
||||
title={g.localTitle}
|
||||
/>
|
||||
<ModeCard
|
||||
active={state.mode === 'cloud'}
|
||||
description={g.cloudDesc}
|
||||
disabled={state.envOverride}
|
||||
icon={Cloud}
|
||||
onSelect={() => setState(current => ({ ...current, mode: 'cloud' }))}
|
||||
title={g.cloudTitle}
|
||||
/>
|
||||
{cloudBetaEnabled ? (
|
||||
<ModeCard
|
||||
active={state.mode === 'cloud'}
|
||||
description={g.cloudDesc}
|
||||
disabled={state.envOverride}
|
||||
icon={Cloud}
|
||||
onSelect={() => setState(current => ({ ...current, mode: 'cloud' }))}
|
||||
title={g.cloudTitle}
|
||||
/>
|
||||
) : null}
|
||||
<ModeCard
|
||||
active={state.mode === 'remote'}
|
||||
description={g.remoteDesc}
|
||||
|
|
|
|||
2
apps/desktop/src/global.d.ts
vendored
2
apps/desktop/src/global.d.ts
vendored
|
|
@ -59,6 +59,8 @@ declare global {
|
|||
// sign-in (cloud-auto-discovery Phase 3).
|
||||
cloud: {
|
||||
status: () => Promise<DesktopCloudStatus>
|
||||
// Whether the BETA env flag enables the Hermes Cloud gateway selector.
|
||||
betaEnabled: () => Promise<boolean>
|
||||
login: () => Promise<DesktopCloudStatus & { ok: boolean }>
|
||||
logout: () => Promise<DesktopCloudStatus & { ok: boolean }>
|
||||
discover: (org?: string) => Promise<DesktopCloudDiscoverResult>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue