From 944dde36079f85badefd9bc7dea39fe2c982bc84 Mon Sep 17 00:00:00 2001 From: Steven Enamakel <31011319+senamakel@users.noreply.github.com> Date: Thu, 21 May 2026 01:26:48 -0700 Subject: [PATCH 1/7] Update Product Hunt badges in README --- README.md | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 68501bfca..f34d7a4c1 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,26 @@

- - tinyhumansai%2Fopenhuman | Trendshift - - - OpenHuman - An open source AI harness built with the human in mind | Product Hunt - - - OpenHuman - An open source AI harness built with the human in mind | Product Hunt + + tinyhumansai%2Fopenhuman | Trendshift + + + OpenHuman - An open source AI harness built with the human in mind | Product Hunt + + + OpenHuman - An open source AI harness built with the human in mind | Product Hunt +

- +

+ + OpenHuman - An open source AI harness built with the human in mind | Product Hunt + + + OpenHuman - An open source AI harness built with the human in mind | Product Hunt + +

+ +

OpenHuman is your Personal AI super intelligence. Private, Simple and extremely powerful.

From 182c01f3a92840c96c2ebcede2d121320ccac432 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 20:15:23 +0530 Subject: [PATCH 2/7] fix(auth): deliver OAuth JWT to remote core in cloud mode Two failure paths prevented the openhuman://auth deep link from reaching a Docker-hosted remote core: 1. oauthAuthReadiness.ts pinged the core with a stale local-core bearer token (resolved from cache). Fix: explicitly pass the stored cloud token to testCoreRpcConnection in cloud mode. 2. CoreStateProvider's auth-expired cascade cleared the session while auth_store_session was in flight. Fix: dispatch a 15 s suppress- reauth window around storeSession; CoreStateProvider skips clearSession while the window is active. Also busts stale RPC URL/token caches before auth_store_session in cloud mode, and improves the core_unreachable error message to name the cloud core specifically. Closes #2377 --- .../__tests__/oauthAuthReadiness.test.ts | 33 +++++++++++- .../components/oauth/oauthAuthReadiness.ts | 20 +++++-- app/src/providers/CoreStateProvider.tsx | 21 ++++++++ app/src/services/coreRpcClient.ts | 6 +++ .../__tests__/desktopDeepLinkListener.test.ts | 54 +++++++++++++++++++ app/src/utils/desktopDeepLinkListener.ts | 23 +++++++- 6 files changed, 151 insertions(+), 6 deletions(-) diff --git a/app/src/components/oauth/__tests__/oauthAuthReadiness.test.ts b/app/src/components/oauth/__tests__/oauthAuthReadiness.test.ts index 56bbcbeb6..53ac95982 100644 --- a/app/src/components/oauth/__tests__/oauthAuthReadiness.test.ts +++ b/app/src/components/oauth/__tests__/oauthAuthReadiness.test.ts @@ -4,7 +4,7 @@ import { getCoreStateSnapshot } from '../../../lib/coreState/store'; import { bootCheckTransport } from '../../../services/bootCheckService'; import { testCoreRpcConnection } from '../../../services/coreRpcClient'; import { isTauri } from '../../../services/webviewAccountService'; -import { getStoredCoreMode } from '../../../utils/configPersistence'; +import { getStoredCoreMode, getStoredCoreToken } from '../../../utils/configPersistence'; import { oauthAuthReadinessUserMessage, prepareOAuthLoginLaunch, @@ -22,7 +22,10 @@ vi.mock('../../../services/bootCheckService', () => ({ bootCheckTransport: { invokeCmd: vi.fn().mockResolvedValue(undefined), callRpc: vi.fn() }, })); -vi.mock('../../../utils/configPersistence', () => ({ getStoredCoreMode: vi.fn() })); +vi.mock('../../../utils/configPersistence', () => ({ + getStoredCoreMode: vi.fn(), + getStoredCoreToken: vi.fn().mockReturnValue(null), +})); vi.mock('../../../services/webviewAccountService', () => ({ isTauri: vi.fn().mockReturnValue(true), @@ -135,4 +138,30 @@ describe('oauthAuthReadiness', () => { vi.useRealTimers(); } }); + + it('returns cloud-specific message for core_unreachable when mode is cloud', () => { + vi.mocked(getStoredCoreMode).mockReturnValue('cloud'); + const msg = oauthAuthReadinessUserMessage('core_unreachable'); + expect(msg).toMatch(/remote.*cloud/i); + expect(msg).toMatch(/RPC URL/i); + }); + + it('returns local-specific message for core_unreachable when mode is local', () => { + vi.mocked(getStoredCoreMode).mockReturnValue('local'); + const msg = oauthAuthReadinessUserMessage('core_unreachable'); + expect(msg).toMatch(/local runtime/i); + expect(msg).toMatch(/Quit and reopen/i); + }); + + it('passes cloud token to testCoreRpcConnection when mode is cloud', async () => { + vi.mocked(getStoredCoreMode).mockReturnValue('cloud'); + vi.mocked(getStoredCoreToken).mockReturnValue('cloud-bearer-token'); + + await waitForOAuthAuthReadiness(2_000); + + expect(testCoreRpcConnection).toHaveBeenCalledWith( + 'http://127.0.0.1:7788/rpc', + 'cloud-bearer-token' + ); + }); }); diff --git a/app/src/components/oauth/oauthAuthReadiness.ts b/app/src/components/oauth/oauthAuthReadiness.ts index 3d519c4eb..63ba50372 100644 --- a/app/src/components/oauth/oauthAuthReadiness.ts +++ b/app/src/components/oauth/oauthAuthReadiness.ts @@ -4,7 +4,7 @@ import { getCoreStateSnapshot } from '../../lib/coreState/store'; import { bootCheckTransport } from '../../services/bootCheckService'; import { getCoreRpcUrl, testCoreRpcConnection } from '../../services/coreRpcClient'; import { isTauri } from '../../services/webviewAccountService'; -import { getStoredCoreMode } from '../../utils/configPersistence'; +import { getStoredCoreMode, getStoredCoreToken } from '../../utils/configPersistence'; const logPrefix = '[oauth-auth-readiness]'; const log = debug('oauth:auth-readiness'); @@ -27,7 +27,13 @@ const delay = (ms: number): Promise => async function pingCoreRpc(): Promise { try { const rpcUrl = await getCoreRpcUrl(); - const response = await testCoreRpcConnection(rpcUrl); + // In cloud mode, pass the stored cloud token explicitly to avoid + // getCoreRpcToken() resolving to a stale local-core token. See issue #2377. + const cloudToken = getStoredCoreMode() === 'cloud' ? getStoredCoreToken() : null; + log(`${logPrefix} core.ping probe`, { rpcUrl, mode: getStoredCoreMode(), hasCloudToken: Boolean(cloudToken) }); + const response = cloudToken + ? await testCoreRpcConnection(rpcUrl, cloudToken) + : await testCoreRpcConnection(rpcUrl); return response.ok; } catch (err) { log(`${logPrefix} core.ping probe failed`, err); @@ -112,11 +118,19 @@ export function oauthAuthReadinessUserMessage(reason: OAuthAuthReadinessFailure) 'Finish choosing how OpenHuman runs (tap Continue on the setup screen), ' + 'then try signing in again.' ); - case 'core_unreachable': + case 'core_unreachable': { + const mode = getStoredCoreMode(); + if (mode === 'cloud') { + return ( + 'OpenHuman could not reach its remote (cloud) runtime. ' + + 'Check your RPC URL and token in Settings, then try signing in again.' + ); + } return ( 'OpenHuman could not reach its local runtime. Quit and reopen the app, ' + 'then try signing in again.' ); + } default: return 'Sign-in is still starting up. Wait a few seconds and try again.'; } diff --git a/app/src/providers/CoreStateProvider.tsx b/app/src/providers/CoreStateProvider.tsx index 42ca0abca..e994909b2 100644 --- a/app/src/providers/CoreStateProvider.tsx +++ b/app/src/providers/CoreStateProvider.tsx @@ -617,6 +617,23 @@ export default function CoreStateProvider({ children }: { children: ReactNode }) ); const lastReauthAtRef = useRef(0); + const suppressReauthUntilRef = useRef(0); + + // Listen for deep-link auth suppression signals so that an in-flight + // `auth_store_session` call (OAuth deep link) does not race with the + // `core-rpc-auth-expired` handler and clear the session mid-delivery. + // See issue #2377. + useEffect(() => { + const onSuppressReauth = (event: Event) => { + const until = (event as CustomEvent<{ until: number }>).detail?.until ?? 0; + suppressReauthUntilRef.current = until; + log('[CoreState] suppress-reauth updated until=%d', until); + }; + window.addEventListener('core-state:suppress-reauth', onSuppressReauth as EventListener); + return () => { + window.removeEventListener('core-state:suppress-reauth', onSuppressReauth as EventListener); + }; + }, []); const clearSession = useCallback(async () => { logoutGuardUntilRef.current = Date.now() + 5_000; @@ -665,6 +682,10 @@ export default function CoreStateProvider({ children }: { children: ReactNode }) useEffect(() => { const runReauth = (method: string, source: string) => { const now = Date.now(); + if (Date.now() < suppressReauthUntilRef.current) { + log('[CoreState] auth-expired suppressed during deep-link auth delivery (method=%s source=%s)', method, source); + return; + } if (now - lastReauthAtRef.current < 10_000) { log('auth-expired debounced (method=%s source=%s)', method, source); return; diff --git a/app/src/services/coreRpcClient.ts b/app/src/services/coreRpcClient.ts index 610b3fecb..e3bf0e843 100644 --- a/app/src/services/coreRpcClient.ts +++ b/app/src/services/coreRpcClient.ts @@ -465,6 +465,12 @@ export async function callCoreRpc({ try { const [rpcUrl, token] = await Promise.all([getCoreRpcUrl(), getCoreRpcToken()]); coreRpcLog('HTTP request', { id: payload.id, method: payload.method }); + if (normalizedMethod === 'openhuman.auth_store_session') { + coreRpcLog('[rpc] auth_store_session routing', { + rpcUrl, + tokenSource: getStoredCoreToken() ? 'cloud-stored' : 'local-resolved', + }); + } if (coreIsTauri() && !token) { throw new Error('Core RPC token unavailable in Tauri; local RPC auth cannot be satisfied'); } diff --git a/app/src/utils/__tests__/desktopDeepLinkListener.test.ts b/app/src/utils/__tests__/desktopDeepLinkListener.test.ts index 41665241d..eb41269c9 100644 --- a/app/src/utils/__tests__/desktopDeepLinkListener.test.ts +++ b/app/src/utils/__tests__/desktopDeepLinkListener.test.ts @@ -2,14 +2,22 @@ import { isTauri } from '@tauri-apps/api/core'; import { getCurrent, onOpenUrl } from '@tauri-apps/plugin-deep-link'; import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { clearCoreRpcTokenCache, clearCoreRpcUrlCache } from '../../services/coreRpcClient'; import { completeDeepLinkAuthProcessing, getDeepLinkAuthState, subscribeDeepLinkAuthState, } from '../../store/deepLinkAuthState'; +import { getStoredCoreMode } from '../configPersistence'; import { setupDesktopDeepLinkListener } from '../desktopDeepLinkListener'; import { storeSession } from '../tauriCommands'; +vi.mock('../configPersistence', () => ({ getStoredCoreMode: vi.fn() })); +vi.mock('../../services/coreRpcClient', () => ({ + clearCoreRpcUrlCache: vi.fn(), + clearCoreRpcTokenCache: vi.fn(), +})); + const waitForAuthSettled = (): Promise => new Promise(resolve => { if (!getDeepLinkAuthState().isProcessing) { @@ -59,6 +67,9 @@ describe('desktopDeepLinkListener', () => { waitForOAuthAuthReadiness.mockResolvedValue({ ready: true }); vi.mocked(storeSession).mockReset(); vi.mocked(storeSession).mockResolvedValue(undefined); + vi.mocked(getStoredCoreMode).mockReturnValue(null); + vi.mocked(clearCoreRpcUrlCache).mockClear(); + vi.mocked(clearCoreRpcTokenCache).mockClear(); windowControls.show.mockClear(); windowControls.unminimize.mockClear(); windowControls.setFocus.mockClear(); @@ -194,4 +205,47 @@ describe('desktopDeepLinkListener', () => { 'OAuth sign-in failed before OpenHuman received authorization. Check the provider app settings and try again.', }); }); + + it('busts RPC caches before storeSession in cloud mode', async () => { + vi.mocked(getStoredCoreMode).mockReturnValue('cloud'); + vi.mocked(getCurrent).mockResolvedValue(['openhuman://auth?token=abc&key=auth']); + + await setupDesktopDeepLinkListener(); + await waitForAuthSettled(); + + expect(clearCoreRpcUrlCache).toHaveBeenCalledTimes(1); + expect(clearCoreRpcTokenCache).toHaveBeenCalledTimes(1); + expect(storeSession).toHaveBeenCalledWith('abc', {}); + }); + + it('does NOT bust RPC caches before storeSession in local mode', async () => { + vi.mocked(getStoredCoreMode).mockReturnValue('local'); + vi.mocked(getCurrent).mockResolvedValue(['openhuman://auth?token=abc&key=auth']); + + await setupDesktopDeepLinkListener(); + await waitForAuthSettled(); + + expect(clearCoreRpcUrlCache).not.toHaveBeenCalled(); + expect(clearCoreRpcTokenCache).not.toHaveBeenCalled(); + expect(storeSession).toHaveBeenCalledWith('abc', {}); + }); + + it('dispatches suppress-reauth before storeSession and clears it after in cloud mode', async () => { + vi.mocked(getStoredCoreMode).mockReturnValue('cloud'); + vi.mocked(getCurrent).mockResolvedValue(['openhuman://auth?token=abc&key=auth']); + + const suppressEvents: Array<{ until: number }> = []; + window.addEventListener('core-state:suppress-reauth', event => { + suppressEvents.push((event as CustomEvent<{ until: number }>).detail); + }); + + await setupDesktopDeepLinkListener(); + await waitForAuthSettled(); + + // First event: non-zero until (suppress on) + expect(suppressEvents.length).toBeGreaterThanOrEqual(2); + expect(suppressEvents[0].until).toBeGreaterThan(0); + // Last event: until=0 (suppress cleared) + expect(suppressEvents[suppressEvents.length - 1].until).toBe(0); + }); }); diff --git a/app/src/utils/desktopDeepLinkListener.ts b/app/src/utils/desktopDeepLinkListener.ts index 254b6f96d..b00221a4e 100644 --- a/app/src/utils/desktopDeepLinkListener.ts +++ b/app/src/utils/desktopDeepLinkListener.ts @@ -4,11 +4,13 @@ import { getCurrent, onOpenUrl } from '@tauri-apps/plugin-deep-link'; import { patchCoreStateSnapshot } from '../lib/coreState/store'; import { consumeLoginToken } from '../services/api/authApi'; +import { clearCoreRpcTokenCache, clearCoreRpcUrlCache } from '../services/coreRpcClient'; import { beginDeepLinkAuthProcessing, completeDeepLinkAuthProcessing, failDeepLinkAuthProcessing, } from '../store/deepLinkAuthState'; +import { getStoredCoreMode } from './configPersistence'; import { BILLING_DASHBOARD_URL } from './links'; import { evaluateOAuthAppVersionGate, @@ -76,7 +78,26 @@ const focusMainWindow = async () => { }; const applySessionToken = async (sessionToken: string): Promise => { - await storeSession(sessionToken, {}); + // In cloud mode, bust any stale RPC URL/token caches so auth_store_session + // targets the user's configured remote core. See issue #2377. + const currentCoreMode = getStoredCoreMode(); + if (currentCoreMode === 'cloud') { + console.debug('[DeepLink] cloud mode: busting RPC caches before session delivery'); + clearCoreRpcUrlCache(); + clearCoreRpcTokenCache(); + } + + // Signal CoreStateProvider to hold off clearing session during token delivery. + window.dispatchEvent( + new CustomEvent('core-state:suppress-reauth', { detail: { until: Date.now() + 15_000 } }) + ); + try { + await storeSession(sessionToken, {}); + } finally { + window.dispatchEvent( + new CustomEvent('core-state:suppress-reauth', { detail: { until: 0 } }) + ); + } patchCoreStateSnapshot({ snapshot: { sessionToken } }); window.dispatchEvent(new CustomEvent(SESSION_TOKEN_UPDATED_EVENT, { detail: { sessionToken } })); }; From 265d0ac1b4c680f2bcdbebf3de830c525c1d405a Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 22:27:26 +0530 Subject: [PATCH 3/7] chore: fix Prettier formatting on oauth readiness + deep-link files --- app/src/components/oauth/oauthAuthReadiness.ts | 6 +++++- app/src/providers/CoreStateProvider.tsx | 6 +++++- app/src/utils/desktopDeepLinkListener.ts | 4 +--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/src/components/oauth/oauthAuthReadiness.ts b/app/src/components/oauth/oauthAuthReadiness.ts index 63ba50372..154295fb0 100644 --- a/app/src/components/oauth/oauthAuthReadiness.ts +++ b/app/src/components/oauth/oauthAuthReadiness.ts @@ -30,7 +30,11 @@ async function pingCoreRpc(): Promise { // In cloud mode, pass the stored cloud token explicitly to avoid // getCoreRpcToken() resolving to a stale local-core token. See issue #2377. const cloudToken = getStoredCoreMode() === 'cloud' ? getStoredCoreToken() : null; - log(`${logPrefix} core.ping probe`, { rpcUrl, mode: getStoredCoreMode(), hasCloudToken: Boolean(cloudToken) }); + log(`${logPrefix} core.ping probe`, { + rpcUrl, + mode: getStoredCoreMode(), + hasCloudToken: Boolean(cloudToken), + }); const response = cloudToken ? await testCoreRpcConnection(rpcUrl, cloudToken) : await testCoreRpcConnection(rpcUrl); diff --git a/app/src/providers/CoreStateProvider.tsx b/app/src/providers/CoreStateProvider.tsx index e994909b2..cfffdcc1b 100644 --- a/app/src/providers/CoreStateProvider.tsx +++ b/app/src/providers/CoreStateProvider.tsx @@ -683,7 +683,11 @@ export default function CoreStateProvider({ children }: { children: ReactNode }) const runReauth = (method: string, source: string) => { const now = Date.now(); if (Date.now() < suppressReauthUntilRef.current) { - log('[CoreState] auth-expired suppressed during deep-link auth delivery (method=%s source=%s)', method, source); + log( + '[CoreState] auth-expired suppressed during deep-link auth delivery (method=%s source=%s)', + method, + source + ); return; } if (now - lastReauthAtRef.current < 10_000) { diff --git a/app/src/utils/desktopDeepLinkListener.ts b/app/src/utils/desktopDeepLinkListener.ts index b00221a4e..d346b99e9 100644 --- a/app/src/utils/desktopDeepLinkListener.ts +++ b/app/src/utils/desktopDeepLinkListener.ts @@ -94,9 +94,7 @@ const applySessionToken = async (sessionToken: string): Promise => { try { await storeSession(sessionToken, {}); } finally { - window.dispatchEvent( - new CustomEvent('core-state:suppress-reauth', { detail: { until: 0 } }) - ); + window.dispatchEvent(new CustomEvent('core-state:suppress-reauth', { detail: { until: 0 } })); } patchCoreStateSnapshot({ snapshot: { sessionToken } }); window.dispatchEvent(new CustomEvent(SESSION_TOKEN_UPDATED_EVENT, { detail: { sessionToken } })); From e7590148dea8c41b423f01b26465919eb9b6fcf4 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 22:55:20 +0530 Subject: [PATCH 4/7] test(core-state): cover suppress-reauth window from deep-link auth delivery (#2377) Add two targeted tests that exercise the `core-state:suppress-reauth` custom-event handler introduced for issue #2377: - verifies auth-expired clearSession is blocked while the suppress window is active - verifies clearSession resumes after the window is explicitly cleared (until=0) --- .../__tests__/CoreStateProvider.test.tsx | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/app/src/providers/__tests__/CoreStateProvider.test.tsx b/app/src/providers/__tests__/CoreStateProvider.test.tsx index 65b53218a..3f74572de 100644 --- a/app/src/providers/__tests__/CoreStateProvider.test.tsx +++ b/app/src/providers/__tests__/CoreStateProvider.test.tsx @@ -550,6 +550,79 @@ describe('CoreStateProvider — identity-change cache clearing', () => { expect(vi.mocked(tauriCommands.logout)).toHaveBeenCalledTimes(1); }); + it('core-state:suppress-reauth suppresses auth-expired clearSession during deep-link delivery (#2377)', async () => { + fetchSnapshot.mockResolvedValue(makeSnapshot({ userId: 'u1', sessionToken: 'tok1' })); + listTeams.mockResolvedValue([]); + vi.mocked(tauriCommands.logout).mockReset(); + vi.mocked(tauriCommands.logout).mockResolvedValue(undefined as never); + + render( + + + + ); + + await waitFor(() => expect(screen.getByTestId('ready').textContent).toBe('ready')); + + // Arm the suppress window so core-rpc-auth-expired is silenced. + await act(async () => { + window.dispatchEvent( + new CustomEvent('core-state:suppress-reauth', { + detail: { until: Date.now() + 30_000 }, + }) + ); + }); + + // auth-expired during the suppress window must not call logout. + await act(async () => { + window.dispatchEvent( + new CustomEvent('core-rpc-auth-expired', { + detail: { method: 'openhuman.auth_store_session', source: 'rpc' }, + }) + ); + }); + + expect(vi.mocked(tauriCommands.logout)).not.toHaveBeenCalled(); + }); + + it('core-state:suppress-reauth with until=0 re-enables auth-expired handling after deep-link delivery (#2377)', async () => { + fetchSnapshot.mockResolvedValue(makeSnapshot({ userId: 'u1', sessionToken: 'tok1' })); + listTeams.mockResolvedValue([]); + vi.mocked(tauriCommands.logout).mockReset(); + vi.mocked(tauriCommands.logout).mockResolvedValue(undefined as never); + + render( + + + + ); + + await waitFor(() => expect(screen.getByTestId('ready').textContent).toBe('ready')); + + // Arm then immediately disarm so clearSession is allowed again. + await act(async () => { + window.dispatchEvent( + new CustomEvent('core-state:suppress-reauth', { detail: { until: Date.now() + 30_000 } }) + ); + }); + await act(async () => { + window.dispatchEvent( + new CustomEvent('core-state:suppress-reauth', { detail: { until: 0 } }) + ); + }); + + // auth-expired after suppress cleared must call logout. + await act(async () => { + window.dispatchEvent( + new CustomEvent('core-rpc-auth-expired', { + detail: { method: 'openhuman.team_get_usage', source: 'rpc' }, + }) + ); + }); + + await waitFor(() => expect(vi.mocked(tauriCommands.logout)).toHaveBeenCalledTimes(1)); + }); + it('ignores forged session-token-updated events that do not match the core snapshot (#1937)', async () => { fetchSnapshot.mockResolvedValue(makeSnapshot({ userId: 'u1', sessionToken: 'tok1' })); listTeams.mockResolvedValue([]); From f2a0a2f6ed35c8b7edb7acf8c5eba94a191bf028 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 22:57:06 +0530 Subject: [PATCH 5/7] chore(format): prettier fix for CoreStateProvider test additions --- app/src/providers/__tests__/CoreStateProvider.test.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/src/providers/__tests__/CoreStateProvider.test.tsx b/app/src/providers/__tests__/CoreStateProvider.test.tsx index 3f74572de..ea4aae915 100644 --- a/app/src/providers/__tests__/CoreStateProvider.test.tsx +++ b/app/src/providers/__tests__/CoreStateProvider.test.tsx @@ -567,9 +567,7 @@ describe('CoreStateProvider — identity-change cache clearing', () => { // Arm the suppress window so core-rpc-auth-expired is silenced. await act(async () => { window.dispatchEvent( - new CustomEvent('core-state:suppress-reauth', { - detail: { until: Date.now() + 30_000 }, - }) + new CustomEvent('core-state:suppress-reauth', { detail: { until: Date.now() + 30_000 } }) ); }); @@ -606,9 +604,7 @@ describe('CoreStateProvider — identity-change cache clearing', () => { ); }); await act(async () => { - window.dispatchEvent( - new CustomEvent('core-state:suppress-reauth', { detail: { until: 0 } }) - ); + window.dispatchEvent(new CustomEvent('core-state:suppress-reauth', { detail: { until: 0 } })); }); // auth-expired after suppress cleared must call logout. From ea912f6e6e8bc1f8ee7238b6817dded7bc331571 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Fri, 22 May 2026 17:53:54 -0700 Subject: [PATCH 6/7] refactor(core-state): reuse computed now in runReauth suppression check Trivial cleanup: the suppress-reauth check called Date.now() a second time instead of reusing the 'now' value computed on the line above. Functionally identical, but consistent with the debounce check below. --- app/src/providers/CoreStateProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/providers/CoreStateProvider.tsx b/app/src/providers/CoreStateProvider.tsx index cfffdcc1b..45944dfe6 100644 --- a/app/src/providers/CoreStateProvider.tsx +++ b/app/src/providers/CoreStateProvider.tsx @@ -682,7 +682,7 @@ export default function CoreStateProvider({ children }: { children: ReactNode }) useEffect(() => { const runReauth = (method: string, source: string) => { const now = Date.now(); - if (Date.now() < suppressReauthUntilRef.current) { + if (now < suppressReauthUntilRef.current) { log( '[CoreState] auth-expired suppressed during deep-link auth delivery (method=%s source=%s)', method, From 868eb7bd8e9ae4a5a64417a4a3af701199840cd2 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Fri, 22 May 2026 18:07:40 -0700 Subject: [PATCH 7/7] Merge upstream/main: drop duplicate de-5 mcpServer keys --- app/src/lib/i18n/chunks/de-5.ts | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/app/src/lib/i18n/chunks/de-5.ts b/app/src/lib/i18n/chunks/de-5.ts index 79f041cc1..c8a26af5f 100644 --- a/app/src/lib/i18n/chunks/de-5.ts +++ b/app/src/lib/i18n/chunks/de-5.ts @@ -523,28 +523,6 @@ const de5: TranslationMap = { 'settings.mascot.colorYellow': 'Gelb', 'settings.mascot.libraryUnavailable': 'OpenHuman Bibliothek nicht verfügbar', 'settings.mascot.title': 'OpenHuman', - 'settings.developerMenu.mcpServer.title': 'MCP-Server', - 'settings.developerMenu.mcpServer.desc': - 'Externe MCP-Clients zur Verbindung mit OpenHuman konfigurieren', - 'settings.mcpServer.title': 'MCP-Server', - 'settings.mcpServer.toolsSectionTitle': 'Verfügbare Tools', - 'settings.mcpServer.toolsSectionDesc': - 'Tools, die über den MCP-Stdio-Server bereitgestellt werden, wenn openhuman-core mcp ausgeführt wird', - 'settings.mcpServer.configSectionTitle': 'Client-Konfiguration', - 'settings.mcpServer.configSectionDesc': - 'Wählen Sie Ihren MCP-Client aus, um den passenden Konfigurations-Schnipsel zu erzeugen', - 'settings.mcpServer.copySnippet': 'In Zwischenablage kopieren', - 'settings.mcpServer.copied': 'Kopiert!', - 'settings.mcpServer.openConfigFile': 'Konfigurationsdatei öffnen', - 'settings.mcpServer.binaryPathNotFound': - 'OpenHuman-Binary nicht gefunden. Wenn Sie aus dem Quellcode arbeiten, bauen Sie mit: cargo build --bin openhuman-core', - 'settings.mcpServer.openConfigError': 'Konfigurationsdatei konnte nicht geöffnet werden', - 'settings.mcpServer.clientClaudeDesktop': 'Claude Desktop', - 'settings.mcpServer.clientCursor': 'Cursor', - 'settings.mcpServer.clientCodex': 'Codex', - 'settings.mcpServer.clientZed': 'Zed', - 'settings.mcpServer.configFilePath': 'Konfigurationsdatei', - 'settings.mcpServer.clientSelectorAriaLabel': 'MCP-Client-Auswahl', }; export default de5;