feat: overhaul OpenClaw config panels with JSON5 round-trip write engine

- Add json-five crate for JSON5 serialization preserving comments and formatting
- Rewrite openclaw_config.rs with comment-preserving JSON5 read/write engine
- Add Tauri commands: get_openclaw_live_provider, write_openclaw_config_section
- Redesign EnvPanel as full JSON editor with structured error handling
- Add tools.profile selection (minimal/coding/messaging/full) to ToolsPanel
- Add legacy timeout migration support to AgentsDefaultsPanel
- Add OpenClawHealthBanner component for config validation warnings
- Add supporting hooks, mutations, utility functions, and unit tests
This commit is contained in:
Jason
2026-03-06 18:35:23 +08:00
parent b4fdd5fc0d
commit 7e6f803035
18 changed files with 1242 additions and 385 deletions
+13
View File
@@ -18,6 +18,7 @@ export const openclawKeys = {
env: ["openclaw", "env"] as const,
tools: ["openclaw", "tools"] as const,
agentsDefaults: ["openclaw", "agentsDefaults"] as const,
health: ["openclaw", "health"] as const,
};
// ============================================================
@@ -81,6 +82,15 @@ export function useOpenClawAgentsDefaults() {
});
}
export function useOpenClawHealth(enabled: boolean) {
return useQuery({
queryKey: openclawKeys.health,
queryFn: () => openclawApi.scanHealth(),
staleTime: 30_000,
enabled,
});
}
// ============================================================
// Mutation hooks
// ============================================================
@@ -95,6 +105,7 @@ export function useSaveOpenClawEnv() {
mutationFn: (env: OpenClawEnvConfig) => openclawApi.setEnv(env),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: openclawKeys.env });
queryClient.invalidateQueries({ queryKey: openclawKeys.health });
},
});
}
@@ -109,6 +120,7 @@ export function useSaveOpenClawTools() {
mutationFn: (tools: OpenClawToolsConfig) => openclawApi.setTools(tools),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: openclawKeys.tools });
queryClient.invalidateQueries({ queryKey: openclawKeys.health });
},
});
}
@@ -126,6 +138,7 @@ export function useSaveOpenClawAgentsDefaults() {
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: openclawKeys.agentsDefaults });
queryClient.invalidateQueries({ queryKey: openclawKeys.defaultModel });
queryClient.invalidateQueries({ queryKey: openclawKeys.health });
},
});
}
+20 -3
View File
@@ -80,6 +80,9 @@ export function useProviderActions(activeApp: AppId) {
const existingCatalog = (await openclawApi.getModelCatalog()) || {};
const mergedCatalog = { ...existingCatalog, ...modelCatalog };
await openclawApi.setModelCatalog(mergedCatalog);
await queryClient.invalidateQueries({
queryKey: openclawKeys.health,
});
modelsRegistered = true;
}
@@ -88,6 +91,9 @@ export function useProviderActions(activeApp: AppId) {
const existingDefault = await openclawApi.getDefaultModel();
if (!existingDefault?.primary) {
await openclawApi.setDefaultModel(model);
await queryClient.invalidateQueries({
queryKey: openclawKeys.health,
});
}
}
@@ -109,7 +115,7 @@ export function useProviderActions(activeApp: AppId) {
}
}
},
[addProviderMutation, activeApp, t],
[addProviderMutation, activeApp, queryClient, t],
);
// 更新供应商
@@ -255,15 +261,26 @@ export function useProviderActions(activeApp: AppId) {
};
try {
await openclawApi.setDefaultModel(model);
const outcome = await openclawApi.setDefaultModel(model);
await queryClient.invalidateQueries({
queryKey: openclawKeys.defaultModel,
});
await queryClient.invalidateQueries({
queryKey: openclawKeys.health,
});
toast.success(
t("notifications.openclawDefaultModelSet", {
defaultValue: "已设为默认模型",
}),
{ closeButton: true },
{
closeButton: true,
description: outcome.backupPath
? t("openclaw.backupCreated", {
path: outcome.backupPath,
defaultValue: "Backup created: {{path}}",
})
: undefined,
},
);
} catch (error) {
const detail =