From fc573b05f6c95d2e9673b06fd9c65d79317ae49d Mon Sep 17 00:00:00 2001 From: YellowSnnowmann <167776381+YellowSnnowmann@users.noreply.github.com> Date: Mon, 11 May 2026 22:00:05 +0530 Subject: [PATCH] fix(core-rpc): normalize config method wiring and harden startup against config/SQLite edge cases (#1497) --- app/src-tauri/src/lib.rs | 4 +-- .../services/__tests__/coreRpcClient.test.ts | 4 +-- app/src/services/__tests__/rpcMethods.test.ts | 2 ++ app/src/services/coreRpcClient.ts | 4 +-- app/src/services/rpcMethods.ts | 13 ++++++++++ app/src/utils/tauriCommands/config.test.ts | 12 ++++----- app/src/utils/tauriCommands/config.ts | 24 +++++++++--------- src/openhuman/config/schema/load.rs | 25 ++++++++++++++++--- src/openhuman/memory/tree/store.rs | 7 ++++-- 9 files changed, 66 insertions(+), 29 deletions(-) diff --git a/app/src-tauri/src/lib.rs b/app/src-tauri/src/lib.rs index c6ef2bf64..968ee7ebd 100644 --- a/app/src-tauri/src/lib.rs +++ b/app/src-tauri/src/lib.rs @@ -888,7 +888,7 @@ fn setup_tray(app: &AppHandle) -> tauri::Result<()> { "tray_show_window" => { log::info!("[tray] action=show_window source=menu"); if let Err(err) = show_main_window(app) { - log::error!("[tray] failed to show main window from menu: {err}"); + log::warn!("[tray] failed to show main window from menu: {err}"); } } "tray_toggle_mascot" => { @@ -916,7 +916,7 @@ fn setup_tray(app: &AppHandle) -> tauri::Result<()> { { log::info!("[tray] action=show_window source=left_click"); if let Err(err) = show_main_window(tray.app_handle()) { - log::error!("[tray] failed to show main window from tray click: {err}"); + log::warn!("[tray] failed to show main window from tray click: {err}"); } } }) diff --git a/app/src/services/__tests__/coreRpcClient.test.ts b/app/src/services/__tests__/coreRpcClient.test.ts index 42ca2b109..e9e571677 100644 --- a/app/src/services/__tests__/coreRpcClient.test.ts +++ b/app/src/services/__tests__/coreRpcClient.test.ts @@ -423,7 +423,7 @@ describe('coreRpcClient', () => { }); describe('testCoreRpcConnection', () => { - test('POSTs an openhuman.ping JSON-RPC envelope to the supplied URL', async () => { + test('POSTs a core.ping JSON-RPC envelope to the supplied URL', async () => { vi.resetModules(); vi.mocked(isTauri).mockReturnValue(false); const { testCoreRpcConnection } = await import('../coreRpcClient'); @@ -440,7 +440,7 @@ describe('coreRpcClient', () => { expect(JSON.parse(requestInit.body as string)).toMatchObject({ jsonrpc: '2.0', id: 1, - method: 'openhuman.ping', + method: 'core.ping', params: {}, }); }); diff --git a/app/src/services/__tests__/rpcMethods.test.ts b/app/src/services/__tests__/rpcMethods.test.ts index 75056c0a8..13b02ee68 100644 --- a/app/src/services/__tests__/rpcMethods.test.ts +++ b/app/src/services/__tests__/rpcMethods.test.ts @@ -66,6 +66,8 @@ describe('rpcMethods catalog', () => { ].join('\n'); for (const method of Object.values(CORE_RPC_METHODS)) { + // core.* methods (e.g. core.ping) are special dispatch methods, not in the schema catalog. + if (!method.startsWith('openhuman.')) continue; const methodRoot = method.slice('openhuman.'.length); const namespace = methodRoot.startsWith('screen_intelligence_') ? 'screen_intelligence' diff --git a/app/src/services/coreRpcClient.ts b/app/src/services/coreRpcClient.ts index 8c62d5b15..2eec0732c 100644 --- a/app/src/services/coreRpcClient.ts +++ b/app/src/services/coreRpcClient.ts @@ -196,7 +196,7 @@ async function getCoreRpcToken(): Promise { } /** - * Probe an arbitrary core RPC URL with `openhuman.ping`. Used by the + * Probe an arbitrary core RPC URL with `core.ping`. Used by the * Welcome page's "Test Connection" affordance to validate a user-entered * RPC URL without going through the cached `getCoreRpcUrl` resolution. * @@ -221,7 +221,7 @@ export async function testCoreRpcConnection( return fetch(url, { method: 'POST', headers, - body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'openhuman.ping', params: {} }), + body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'core.ping', params: {} }), }); } diff --git a/app/src/services/rpcMethods.ts b/app/src/services/rpcMethods.ts index 4a76d93b7..578f4f0a9 100644 --- a/app/src/services/rpcMethods.ts +++ b/app/src/services/rpcMethods.ts @@ -1,24 +1,37 @@ export const CORE_RPC_METHODS = { configGet: 'openhuman.config_get', + configGetAnalyticsSettings: 'openhuman.config_get_analytics_settings', + configGetComposioTriggerSettings: 'openhuman.config_get_composio_trigger_settings', configGetRuntimeFlags: 'openhuman.config_get_runtime_flags', configSetBrowserAllowAll: 'openhuman.config_set_browser_allow_all', + configUpdateAnalyticsSettings: 'openhuman.config_update_analytics_settings', configUpdateBrowserSettings: 'openhuman.config_update_browser_settings', + configUpdateComposioTriggerSettings: 'openhuman.config_update_composio_trigger_settings', + configUpdateLocalAiSettings: 'openhuman.config_update_local_ai_settings', configUpdateMemorySettings: 'openhuman.config_update_memory_settings', configUpdateModelSettings: 'openhuman.config_update_model_settings', configUpdateRuntimeSettings: 'openhuman.config_update_runtime_settings', configUpdateScreenIntelligenceSettings: 'openhuman.config_update_screen_intelligence_settings', configWorkspaceOnboardingFlagExists: 'openhuman.config_workspace_onboarding_flag_exists', configWorkspaceOnboardingFlagSet: 'openhuman.config_workspace_onboarding_flag_set', + corePing: 'core.ping', screenIntelligenceStatus: 'openhuman.screen_intelligence_status', } as const; export type CoreRpcMethod = (typeof CORE_RPC_METHODS)[keyof typeof CORE_RPC_METHODS]; export const LEGACY_METHOD_ALIASES: Record = { + 'openhuman.get_analytics_settings': CORE_RPC_METHODS.configGetAnalyticsSettings, + 'openhuman.get_composio_trigger_settings': CORE_RPC_METHODS.configGetComposioTriggerSettings, 'openhuman.get_config': CORE_RPC_METHODS.configGet, 'openhuman.get_runtime_flags': CORE_RPC_METHODS.configGetRuntimeFlags, + 'openhuman.ping': CORE_RPC_METHODS.corePing, 'openhuman.set_browser_allow_all': CORE_RPC_METHODS.configSetBrowserAllowAll, + 'openhuman.update_analytics_settings': CORE_RPC_METHODS.configUpdateAnalyticsSettings, 'openhuman.update_browser_settings': CORE_RPC_METHODS.configUpdateBrowserSettings, + 'openhuman.update_composio_trigger_settings': + CORE_RPC_METHODS.configUpdateComposioTriggerSettings, + 'openhuman.update_local_ai_settings': CORE_RPC_METHODS.configUpdateLocalAiSettings, 'openhuman.update_memory_settings': CORE_RPC_METHODS.configUpdateMemorySettings, 'openhuman.update_model_settings': CORE_RPC_METHODS.configUpdateModelSettings, 'openhuman.update_runtime_settings': CORE_RPC_METHODS.configUpdateRuntimeSettings, diff --git a/app/src/utils/tauriCommands/config.test.ts b/app/src/utils/tauriCommands/config.test.ts index 161cbc9da..1befed7dc 100644 --- a/app/src/utils/tauriCommands/config.test.ts +++ b/app/src/utils/tauriCommands/config.test.ts @@ -35,7 +35,7 @@ describe('tauriCommands/config', () => { expect(mockCallCoreRpc).not.toHaveBeenCalled(); }); - test('forwards the patch to openhuman.update_local_ai_settings', async () => { + test('forwards the patch to openhuman.config_update_local_ai_settings', async () => { mockCallCoreRpc.mockResolvedValue({ result: { config: {}, workspace_dir: '/tmp', config_path: '/tmp/cfg.toml' }, logs: [], @@ -43,7 +43,7 @@ describe('tauriCommands/config', () => { const patch = { runtime_enabled: true, usage_embeddings: true, usage_subconscious: false }; await openhumanUpdateLocalAiSettings(patch); expect(mockCallCoreRpc).toHaveBeenCalledWith({ - method: 'openhuman.update_local_ai_settings', + method: 'openhuman.config_update_local_ai_settings', params: patch, }); }); @@ -104,7 +104,7 @@ describe('tauriCommands/config', () => { expect(mockCallCoreRpc).not.toHaveBeenCalled(); }); - test('forwards the patch to openhuman.update_composio_trigger_settings', async () => { + test('forwards the patch to openhuman.config_update_composio_trigger_settings', async () => { mockCallCoreRpc.mockResolvedValue({ result: { config: {}, workspace_dir: '/tmp', config_path: '/tmp/cfg.toml' }, logs: [], @@ -112,7 +112,7 @@ describe('tauriCommands/config', () => { const patch = { triage_disabled: true, triage_disabled_toolkits: ['gmail', 'slack'] }; await openhumanUpdateComposioTriggerSettings(patch); expect(mockCallCoreRpc).toHaveBeenCalledWith({ - method: 'openhuman.update_composio_trigger_settings', + method: 'openhuman.config_update_composio_trigger_settings', params: patch, }); }); @@ -132,14 +132,14 @@ describe('tauriCommands/config', () => { expect(mockCallCoreRpc).not.toHaveBeenCalled(); }); - test('reads via openhuman.get_composio_trigger_settings', async () => { + test('reads via openhuman.config_get_composio_trigger_settings', async () => { mockCallCoreRpc.mockResolvedValue({ result: { triage_disabled: false, triage_disabled_toolkits: ['slack'] }, logs: [], }); const out = await openhumanGetComposioTriggerSettings(); expect(mockCallCoreRpc).toHaveBeenCalledWith({ - method: 'openhuman.get_composio_trigger_settings', + method: 'openhuman.config_get_composio_trigger_settings', }); expect(out.result.triage_disabled).toBe(false); expect(out.result.triage_disabled_toolkits).toEqual(['slack']); diff --git a/app/src/utils/tauriCommands/config.ts b/app/src/utils/tauriCommands/config.ts index d853214c8..fd44a6244 100644 --- a/app/src/utils/tauriCommands/config.ts +++ b/app/src/utils/tauriCommands/config.ts @@ -149,7 +149,7 @@ export async function openhumanUpdateModelSettings( throw new Error('Not running in Tauri'); } return await callCoreRpc>({ - method: 'openhuman.update_model_settings', + method: CORE_RPC_METHODS.configUpdateModelSettings, params: update, }); } @@ -161,7 +161,7 @@ export async function openhumanUpdateMemorySettings( throw new Error('Not running in Tauri'); } return await callCoreRpc>({ - method: 'openhuman.update_memory_settings', + method: CORE_RPC_METHODS.configUpdateMemorySettings, params: update, }); } @@ -173,7 +173,7 @@ export async function openhumanUpdateRuntimeSettings( throw new Error('Not running in Tauri'); } return await callCoreRpc>({ - method: 'openhuman.update_runtime_settings', + method: CORE_RPC_METHODS.configUpdateRuntimeSettings, params: update, }); } @@ -185,7 +185,7 @@ export async function openhumanUpdateBrowserSettings( throw new Error('Not running in Tauri'); } return await callCoreRpc>({ - method: 'openhuman.update_browser_settings', + method: CORE_RPC_METHODS.configUpdateBrowserSettings, params: update, }); } @@ -197,7 +197,7 @@ export async function openhumanUpdateScreenIntelligenceSettings( throw new Error('Not running in Tauri'); } return await callCoreRpc>({ - method: 'openhuman.update_screen_intelligence_settings', + method: CORE_RPC_METHODS.configUpdateScreenIntelligenceSettings, params: update, }); } @@ -209,7 +209,7 @@ export async function openhumanUpdateLocalAiSettings( throw new Error('Not running in Tauri'); } return await callCoreRpc>({ - method: 'openhuman.update_local_ai_settings', + method: 'openhuman.config_update_local_ai_settings', params: update, }); } @@ -221,7 +221,7 @@ export async function openhumanUpdateAnalyticsSettings(update: { throw new Error('Not running in Tauri'); } return await callCoreRpc>({ - method: 'openhuman.update_analytics_settings', + method: CORE_RPC_METHODS.configUpdateAnalyticsSettings, params: update, }); } @@ -233,7 +233,7 @@ export async function openhumanGetAnalyticsSettings(): Promise< throw new Error('Not running in Tauri'); } return await callCoreRpc>({ - method: 'openhuman.get_analytics_settings', + method: CORE_RPC_METHODS.configGetAnalyticsSettings, }); } @@ -277,7 +277,7 @@ export async function openhumanUpdateComposioTriggerSettings( throw new Error('Not running in Tauri'); } return await callCoreRpc>({ - method: 'openhuman.update_composio_trigger_settings', + method: 'openhuman.config_update_composio_trigger_settings', params: update, }); } @@ -289,7 +289,7 @@ export async function openhumanGetComposioTriggerSettings(): Promise< throw new Error('Not running in Tauri'); } return await callCoreRpc>({ - method: 'openhuman.get_composio_trigger_settings', + method: 'openhuman.config_get_composio_trigger_settings', }); } @@ -298,7 +298,7 @@ export async function openhumanGetRuntimeFlags(): Promise>({ - method: 'openhuman.get_runtime_flags', + method: CORE_RPC_METHODS.configGetRuntimeFlags, }); } @@ -309,7 +309,7 @@ export async function openhumanSetBrowserAllowAll( throw new Error('Not running in Tauri'); } return await callCoreRpc>({ - method: 'openhuman.set_browser_allow_all', + method: CORE_RPC_METHODS.configSetBrowserAllowAll, params: { enabled }, }); } diff --git a/src/openhuman/config/schema/load.rs b/src/openhuman/config/schema/load.rs index 4bd2e1411..30de6da5d 100644 --- a/src/openhuman/config/schema/load.rs +++ b/src/openhuman/config/schema/load.rs @@ -548,9 +548,28 @@ impl Config { let contents = fs::read_to_string(&config_path) .await .context("Failed to read config file")?; - let mut config: Config = toml::from_str(&contents).with_context(|| { - format!("Failed to parse config file {}", config_path.display()) - })?; + let parse_result: Result = toml::from_str(&contents); + let mut config: Config = match parse_result { + Ok(c) => c, + Err(parse_err) => { + let backup_path = config_path.with_extension("toml.bak"); + tracing::warn!( + path = %config_path.display(), + backup = %backup_path.display(), + error = %parse_err, + "[config] Config file is corrupted — backing up and resetting to defaults" + ); + if let Err(copy_err) = fs::copy(&config_path, &backup_path).await { + tracing::warn!( + path = %config_path.display(), + backup = %backup_path.display(), + error = %copy_err, + "[config] Failed to back up corrupted config; continuing with defaults" + ); + } + Config::default() + } + }; config.config_path = config_path.clone(); config.workspace_dir = workspace_dir; migrate_legacy_autocomplete_disabled_apps(&mut config); diff --git a/src/openhuman/memory/tree/store.rs b/src/openhuman/memory/tree/store.rs index 2451facf4..eebc14a76 100644 --- a/src/openhuman/memory/tree/store.rs +++ b/src/openhuman/memory/tree/store.rs @@ -665,8 +665,11 @@ pub(crate) fn with_connection( .with_context(|| format!("Failed to open memory_tree DB: {}", db_path.display()))?; conn.busy_timeout(SQLITE_BUSY_TIMEOUT) .context("Failed to configure memory_tree busy timeout")?; - conn.execute_batch("PRAGMA journal_mode=WAL;") - .context("Failed to enable memory_tree WAL mode")?; + if let Err(wal_err) = conn.execute_batch("PRAGMA journal_mode=WAL;") { + log::warn!( + "[memory_tree] Failed to enable WAL mode (filesystem may not support it): {wal_err}" + ); + } conn.execute_batch(SCHEMA) .context("Failed to initialize memory_tree schema")?; // Phase 2 migrations — additive, idempotent.