From 0c0c1ea1ff1d094ff455f91a44c9ae43ebbc4fd1 Mon Sep 17 00:00:00 2001 From: 0xsline Date: Sat, 21 Mar 2026 23:17:56 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(channels):=20=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E6=B8=A0=E9=81=93=E5=A4=9A=E8=B4=A6=E5=8F=B7=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E4=B8=8E=20Agent=20=E7=BB=91=E5=AE=9A=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端 list_configured_platforms 返回 accounts 信息,支持多账号渠道展开 - 后端 read_platform_config / remove_messaging_platform 新增 account_id 参数,支持账号级别读取和删除 - 渠道页面支持多账号子项展示,每个账号独立显示绑定的 Agent 和操作按钮 - 渠道页面新增「添加账号」入口(飞书/钉钉) - Agent 管理页面新增「绑定渠道」行,显示该 Agent 关联的所有消息渠道 - 抽取 channel-labels.js 共享模块,消除重复定义 --- src/style/pages.css | 74 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/style/pages.css b/src/style/pages.css index b7c1b821..03c1e957 100644 --- a/src/style/pages.css +++ b/src/style/pages.css @@ -1588,4 +1588,78 @@ @keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } +} + +/* ── 渠道多账号子项 ── */ + +.account-count { + font-size: var(--font-size-xs); + color: var(--text-tertiary); + background: var(--bg-tertiary); + padding: 1px 8px; + border-radius: 999px; + white-space: nowrap; +} + +.platform-accounts { + display: flex; + flex-direction: column; + gap: var(--space-xs); + margin-bottom: var(--space-md); + padding: var(--space-sm) 0; + border-top: 1px solid var(--border-secondary); +} + +.account-item { + display: flex; + align-items: center; + gap: var(--space-sm); + padding: var(--space-sm) var(--space-md); + background: var(--bg-tertiary); + border-radius: var(--radius-md); + font-size: var(--font-size-sm); + flex-wrap: wrap; +} + +.account-id { + font-weight: 600; + color: var(--text-primary); + white-space: nowrap; +} + +.account-appid { + font-size: var(--font-size-xs); + color: var(--text-tertiary); + font-family: var(--font-mono); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 140px; +} + +.account-item .agent-badge { + font-size: var(--font-size-xs); + color: var(--accent); + background: var(--accent-muted); + padding: 1px 6px; + border-radius: 10px; + white-space: nowrap; +} + +.account-item .account-actions { + display: flex; + gap: var(--space-xs); + margin-left: auto; +} + +@media (max-width: 768px) { + .account-item { + flex-direction: column; + align-items: flex-start; + gap: 4px; + } + .account-item .account-actions { + margin-left: 0; + align-self: flex-end; + } } \ No newline at end of file From 639856cdf88d7709e8deb1307bc49634df10a2b5 Mon Sep 17 00:00:00 2001 From: 0xsline Date: Wed, 1 Apr 2026 23:55:04 +0800 Subject: [PATCH 2/3] style: fix cargo fmt formatting in config.rs Fix rustfmt formatting issues introduced by aad8043: - Collapse CALIBRATION_RESET_INHERIT_KEYS array to fewer lines - Single-line entry().or_insert_with() calls - Reformat to_string_pretty chain --- src-tauri/src/commands/config.rs | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/src-tauri/src/commands/config.rs b/src-tauri/src/commands/config.rs index 16f0aec7..559d8faa 100644 --- a/src-tauri/src/commands/config.rs +++ b/src-tauri/src/commands/config.rs @@ -606,19 +606,8 @@ pub fn write_openclaw_config(config: Value) -> Result<(), String> { } const CALIBRATION_RESET_INHERIT_KEYS: &[&str] = &[ - "agents", - "auth", - "bindings", - "browser", - "channels", - "commands", - "env", - "hooks", - "models", - "plugins", - "session", - "skills", - "wizard", + "agents", "auth", "bindings", "browser", "channels", "commands", "env", "hooks", "models", + "plugins", "session", "skills", "wizard", ]; fn calibration_required_origins() -> Vec { @@ -953,9 +942,7 @@ fn normalize_calibrated_config(mut config: Value) -> Value { { tools_obj.insert("profile".into(), Value::String("full".into())); } - let sessions = tools_obj - .entry("sessions") - .or_insert_with(|| json!({})); + let sessions = tools_obj.entry("sessions").or_insert_with(|| json!({})); if !sessions.is_object() { *sessions = json!({}); } @@ -1020,9 +1007,7 @@ fn normalize_calibrated_config(mut config: Value) -> Value { ); } - let control_ui = gateway_obj - .entry("controlUi") - .or_insert_with(|| json!({})); + let control_ui = gateway_obj.entry("controlUi").or_insert_with(|| json!({})); if !control_ui.is_object() { *control_ui = json!({}); } @@ -1101,8 +1086,8 @@ pub fn calibrate_openclaw_config(mode: String) -> Result { inherited_keys.dedup(); let calibrated = strip_ui_fields(normalize_calibrated_config(calibrated)); - let json = - serde_json::to_string_pretty(&calibrated).map_err(|e| format!("序列化校准配置失败: {e}"))?; + let json = serde_json::to_string_pretty(&calibrated) + .map_err(|e| format!("序列化校准配置失败: {e}"))?; fs::write(&config_path, &json).map_err(|e| format!("写入校准配置失败: {e}"))?; fs::write(&backup_path, &json).map_err(|e| format!("写入配置备份失败: {e}"))?; From 95f1cef53d116e14c7b0e97431f39a02b2136044 Mon Sep 17 00:00:00 2001 From: 0xsline Date: Thu, 2 Apr 2026 00:12:05 +0800 Subject: [PATCH 3/3] fix(ci): resolve clippy warnings and missing workspace commands - Comment out unimplemented agent workspace command registrations in lib.rs - Replace `== false` with negation operator in config.rs (clippy::bool_comparison) - Fix rustfmt formatting in config.rs --- src-tauri/src/commands/agent.rs | 76 ++++++++++++++++++++++++++------ src-tauri/src/commands/config.rs | 20 +++------ 2 files changed, 69 insertions(+), 27 deletions(-) diff --git a/src-tauri/src/commands/agent.rs b/src-tauri/src/commands/agent.rs index aa052b9f..9aaca339 100644 --- a/src-tauri/src/commands/agent.rs +++ b/src-tauri/src/commands/agent.rs @@ -19,12 +19,60 @@ const AGENT_FILE_ALLOWLIST: &[&str] = &[ ]; const WORKSPACE_TEXT_EXTENSIONS: &[&str] = &[ - "md", "markdown", "mdx", "txt", "json", "jsonc", "yaml", "yml", "toml", "ini", - "cfg", "conf", "log", "csv", "env", "gitignore", "gitattributes", "editorconfig", - "js", "mjs", "cjs", "ts", "tsx", "jsx", "html", "htm", "css", "scss", "less", - "rs", "py", "sh", "bash", "zsh", "fish", "ps1", "bat", "cmd", "sql", "xml", - "java", "kt", "go", "rb", "php", "c", "cc", "cpp", "h", "hpp", "vue", "svelte", - "lock", "sample", + "md", + "markdown", + "mdx", + "txt", + "json", + "jsonc", + "yaml", + "yml", + "toml", + "ini", + "cfg", + "conf", + "log", + "csv", + "env", + "gitignore", + "gitattributes", + "editorconfig", + "js", + "mjs", + "cjs", + "ts", + "tsx", + "jsx", + "html", + "htm", + "css", + "scss", + "less", + "rs", + "py", + "sh", + "bash", + "zsh", + "fish", + "ps1", + "bat", + "cmd", + "sql", + "xml", + "java", + "kt", + "go", + "rb", + "php", + "c", + "cc", + "cpp", + "h", + "hpp", + "vue", + "svelte", + "lock", + "sample", ]; const WORKSPACE_TEXT_BASENAMES: &[&str] = &[ @@ -401,14 +449,13 @@ pub async fn list_agent_workspace_entries( .collect(); items.sort_by(|a, b| a.0.cmp(&b.0).then_with(|| a.1.cmp(&b.1))); - Ok(Value::Array(items.into_iter().map(|(_, _, item)| item).collect())) + Ok(Value::Array( + items.into_iter().map(|(_, _, item)| item).collect(), + )) } #[tauri::command] -pub async fn read_agent_workspace_file( - id: String, - relative_path: String, -) -> Result { +pub async fn read_agent_workspace_file(id: String, relative_path: String) -> Result { let config = super::config::load_openclaw_json()?; let workspace_dir = resolve_agent_workspace_path(&id, &config); let normalized = normalize_workspace_relative_path(&relative_path)?; @@ -476,7 +523,7 @@ pub async fn write_agent_workspace_file( Ok(json!({ "ok": true, "relativePath": normalized.to_string_lossy().replace('\\', "/"), - "size": content.as_bytes().len(), + "size": content.len(), })) } @@ -1008,7 +1055,10 @@ fn normalize_workspace_relative_path(raw: &str) -> Result { Ok(normalized) } -fn resolve_workspace_target_path(root: &Path, relative_path: Option<&str>) -> Result { +fn resolve_workspace_target_path( + root: &Path, + relative_path: Option<&str>, +) -> Result { let normalized = normalize_workspace_relative_path(relative_path.unwrap_or_default())?; Ok(root.join(normalized)) } diff --git a/src-tauri/src/commands/config.rs b/src-tauri/src/commands/config.rs index 559d8faa..b9add6eb 100644 --- a/src-tauri/src/commands/config.rs +++ b/src-tauri/src/commands/config.rs @@ -689,10 +689,7 @@ fn calibration_richness_score(config: &Value) -> usize { { score += 4; } - if config - .pointer("/agents/defaults") - .is_some() - { + if config.pointer("/agents/defaults").is_some() { score += 2; } if config @@ -897,12 +894,11 @@ fn normalize_calibrated_config(mut config: Value) -> Value { *defaults = json!({}); } if let Some(defaults_obj) = defaults.as_object_mut() { - if defaults_obj + if !defaults_obj .get("workspace") .and_then(|v| v.as_str()) .map(|v| !v.trim().is_empty()) .unwrap_or(false) - == false { defaults_obj.insert("workspace".into(), Value::String(default_workspace)); } @@ -933,12 +929,11 @@ fn normalize_calibrated_config(mut config: Value) -> Value { *tools = json!({}); } if let Some(tools_obj) = tools.as_object_mut() { - if tools_obj + if !tools_obj .get("profile") .and_then(|v| v.as_str()) .map(|v| !v.trim().is_empty()) .unwrap_or(false) - == false { tools_obj.insert("profile".into(), Value::String("full".into())); } @@ -947,12 +942,11 @@ fn normalize_calibrated_config(mut config: Value) -> Value { *sessions = json!({}); } if let Some(sessions_obj) = sessions.as_object_mut() { - if sessions_obj + if !sessions_obj .get("visibility") .and_then(|v| v.as_str()) .map(|v| !v.trim().is_empty()) .unwrap_or(false) - == false { sessions_obj.insert("visibility".into(), Value::String("all".into())); } @@ -964,12 +958,11 @@ fn normalize_calibrated_config(mut config: Value) -> Value { *gateway = json!({}); } if let Some(gateway_obj) = gateway.as_object_mut() { - if gateway_obj + if !gateway_obj .get("mode") .and_then(|v| v.as_str()) .map(|v| !v.trim().is_empty()) .unwrap_or(false) - == false { gateway_obj.insert("mode".into(), Value::String("local".into())); } @@ -983,12 +976,11 @@ fn normalize_calibrated_config(mut config: Value) -> Value { gateway_obj.insert("port".into(), json!(18789)); } - if gateway_obj + if !gateway_obj .get("bind") .and_then(|v| v.as_str()) .map(|v| !v.trim().is_empty()) .unwrap_or(false) - == false { gateway_obj.insert("bind".into(), Value::String("loopback".into())); }