diff --git a/apps/desktop/src/hermes-profile-scope.test.ts b/apps/desktop/src/hermes-profile-scope.test.ts new file mode 100644 index 00000000000..88c920da3de --- /dev/null +++ b/apps/desktop/src/hermes-profile-scope.test.ts @@ -0,0 +1,49 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' + +import { + checkHermesUpdate, + getActionStatus, + getStatus, + restartGateway, + setApiRequestProfile, + updateHermes +} from './hermes' + +// Contract: every backend-targeted action helper must carry the active gateway +// profile, so a multi-profile / global-remote user's restart, status poll, and +// update hit the backend they're actually on — not the primary/default. The +// System-panel "restart does nothing" bug was these helpers dropping it. +describe('backend action helpers are profile-scoped', () => { + const api = vi.fn(async (_req: { path: string; profile?: string }) => ({}) as never) + + beforeEach(() => { + ;(window as { hermesDesktop?: unknown }).hermesDesktop = { api } + api.mockClear() + }) + + afterEach(() => { + setApiRequestProfile(null) + delete (window as { hermesDesktop?: unknown }).hermesDesktop + }) + + const lastProfile = () => api.mock.calls.at(-1)?.[0].profile + + it('omits profile when none is active (single-profile users unaffected)', () => { + void getStatus() + expect(lastProfile()).toBeUndefined() + }) + + it('forwards the active profile to every backend action', () => { + setApiRequestProfile('coder') + + void getStatus() + void restartGateway() + void updateHermes() + void checkHermesUpdate() + void getActionStatus('gateway-restart') + + for (const call of api.mock.calls) { + expect(call[0].profile).toBe('coder') + } + }) +}) diff --git a/apps/desktop/src/hermes.ts b/apps/desktop/src/hermes.ts index e29ca5b5ac1..0e5e26ecc13 100644 --- a/apps/desktop/src/hermes.ts +++ b/apps/desktop/src/hermes.ts @@ -274,6 +274,7 @@ export function getGlobalModelInfo(): Promise { export function getStatus(): Promise { return window.hermesDesktop.api({ + ...profileScoped(), path: '/api/status' }) } @@ -756,6 +757,7 @@ export function setModelAssignment(body: ModelAssignmentRequest): Promise { return window.hermesDesktop.api({ + ...profileScoped(), path: '/api/gateway/restart', method: 'POST' }) @@ -763,6 +765,7 @@ export function restartGateway(): Promise { export function updateHermes(): Promise { return window.hermesDesktop.api({ + ...profileScoped(), path: '/api/hermes/update', method: 'POST' }) @@ -773,12 +776,14 @@ export function updateHermes(): Promise { * distinct from the Electron client clone's git state. */ export function checkHermesUpdate(force = false): Promise { return window.hermesDesktop.api({ + ...profileScoped(), path: `/api/hermes/update/check${force ? '?force=true' : ''}` }) } export function getActionStatus(name: string, lines = 200): Promise { return window.hermesDesktop.api({ + ...profileScoped(), path: `/api/actions/${encodeURIComponent(name)}/status?lines=${Math.max(1, lines)}` }) }