mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
fix(core-rpc): normalize config method wiring and harden startup against config/SQLite edge cases (#1497)
This commit is contained in:
@@ -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'
|
||||
|
||||
@@ -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: {} }),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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']);
|
||||
|
||||
@@ -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 },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user