fix(core-rpc): normalize config method wiring and harden startup against config/SQLite edge cases (#1497)

This commit is contained in:
YellowSnnowmann
2026-05-11 09:30:05 -07:00
committed by GitHub
parent 49398094e8
commit fc573b05f6
9 changed files with 66 additions and 29 deletions
+2 -2
View File
@@ -888,7 +888,7 @@ fn setup_tray(app: &AppHandle<AppRuntime>) -> 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<AppRuntime>) -> 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}");
}
}
})
@@ -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: {},
});
});
@@ -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'
+2 -2
View File
@@ -196,7 +196,7 @@ async function getCoreRpcToken(): Promise<string | null> {
}
/**
* 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: {} }),
});
}
+13
View File
@@ -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<string, CoreRpcMethod> = {
'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,
+6 -6
View File
@@ -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']);
+12 -12
View File
@@ -149,7 +149,7 @@ export async function openhumanUpdateModelSettings(
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<ConfigSnapshot>>({
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<CommandResponse<ConfigSnapshot>>({
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<CommandResponse<ConfigSnapshot>>({
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<CommandResponse<ConfigSnapshot>>({
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<CommandResponse<ConfigSnapshot>>({
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<CommandResponse<ConfigSnapshot>>({
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<CommandResponse<ConfigSnapshot>>({
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<CommandResponse<{ enabled: boolean }>>({
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<CommandResponse<ConfigSnapshot>>({
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<CommandResponse<ComposioTriggerSettings>>({
method: 'openhuman.get_composio_trigger_settings',
method: 'openhuman.config_get_composio_trigger_settings',
});
}
@@ -298,7 +298,7 @@ export async function openhumanGetRuntimeFlags(): Promise<CommandResponse<Runtim
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<RuntimeFlags>>({
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<CommandResponse<RuntimeFlags>>({
method: 'openhuman.set_browser_allow_all',
method: CORE_RPC_METHODS.configSetBrowserAllowAll,
params: { enabled },
});
}
+22 -3
View File
@@ -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<Config, _> = 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);
+5 -2
View File
@@ -665,8 +665,11 @@ pub(crate) fn with_connection<T>(
.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.