fix(openclaw): address code review findings for robustness

- Fix EnvPanel visibleKeys using entry key name instead of array index
  to prevent visibility state corruption after deletion
- Add NaN guard in AgentsDefaultsPanel numeric field parsing
- Validate provider id and models before importing from live config
- Upgrade import failure log level from debug to warn for OpenCode/OpenClaw
This commit is contained in:
Jason
2026-02-08 11:14:46 +08:00
parent eccb95b83d
commit bc87f9d9eb
4 changed files with 30 additions and 8 deletions

View File

@@ -501,7 +501,7 @@ pub fn run() {
log::info!("✓ Imported {count} OpenCode provider(s) from live config");
}
Ok(_) => log::debug!("○ No OpenCode providers found to import"),
Err(e) => log::debug!("○ Failed to import OpenCode providers: {e}"),
Err(e) => log::warn!("○ Failed to import OpenCode providers: {e}"),
}
// 2.2 OMO 配置导入(当数据库中无 OMO provider 时,从本地文件导入)
@@ -534,7 +534,7 @@ pub fn run() {
log::info!("✓ Imported {count} OpenClaw provider(s) from live config");
}
Ok(_) => log::debug!("○ No OpenClaw providers found to import"),
Err(e) => log::debug!("○ Failed to import OpenClaw providers: {e}"),
Err(e) => log::warn!("○ Failed to import OpenClaw providers: {e}"),
}
// 3. 导入 MCP 服务器配置(表空时触发)

View File

@@ -707,6 +707,16 @@ pub fn import_openclaw_providers_from_live(state: &AppState) -> Result<usize, Ap
let existing = state.db.get_all_providers("openclaw")?;
for (id, config) in providers {
// Validate: skip entries with empty id or no models
if id.trim().is_empty() {
log::warn!("Skipping OpenClaw provider with empty id");
continue;
}
if config.models.is_empty() {
log::warn!("Skipping OpenClaw provider '{id}': no models defined");
continue;
}
// Skip if already exists in database
if existing.contains_key(&id) {
log::debug!("OpenClaw provider '{id}' already exists in database, skipping");

View File

@@ -70,17 +70,28 @@ const AgentsDefaultsPanel: React.FC = () => {
};
}
// Optional numeric fields
// Optional fields
if (workspace.trim()) updated.workspace = workspace.trim();
else delete updated.workspace;
if (timeout.trim()) updated.timeout = Number(timeout);
// Numeric fields: validate before saving to avoid NaN
const parseNum = (v: string) => {
const n = Number(v);
return !isNaN(n) && isFinite(n) ? n : undefined;
};
const timeoutNum = timeout.trim() ? parseNum(timeout) : undefined;
if (timeoutNum !== undefined) updated.timeout = timeoutNum;
else delete updated.timeout;
if (contextTokens.trim()) updated.contextTokens = Number(contextTokens);
const ctxNum = contextTokens.trim() ? parseNum(contextTokens) : undefined;
if (ctxNum !== undefined) updated.contextTokens = ctxNum;
else delete updated.contextTokens;
if (maxConcurrent.trim()) updated.maxConcurrent = Number(maxConcurrent);
const concNum = maxConcurrent.trim()
? parseNum(maxConcurrent)
: undefined;
if (concNum !== undefined) updated.maxConcurrent = concNum;
else delete updated.maxConcurrent;
await openclawApi.setAgentsDefaults(updated);

View File

@@ -112,7 +112,8 @@ const EnvPanel: React.FC = () => {
<div className="space-y-3">
{entries.map((entry, index) => {
const sensitive = isApiKey(entry.key);
const visible = visibleKeys.has(`${index}`);
const visibilityId = entry.key || `__new_${index}`;
const visible = visibleKeys.has(visibilityId);
return (
<div key={index} className="flex items-center gap-2">
@@ -138,7 +139,7 @@ const EnvPanel: React.FC = () => {
variant="ghost"
size="icon"
className="flex-shrink-0 h-9 w-9 text-muted-foreground"
onClick={() => toggleVisibility(`${index}`)}
onClick={() => toggleVisibility(visibilityId)}
>
{visible ? (
<EyeOff className="w-4 h-4" />