feat: refresh latest v0.97.0 release payload

This commit is contained in:
coso
2026-03-28 08:33:54 +08:00
parent 71a225cfa2
commit dfbbb3d204
69 changed files with 6291 additions and 5190 deletions
+8
View File
@@ -1562,6 +1562,14 @@ pub fn run() {
commands::browser_environment_cmd::save_browser_environment_preset_cmd,
commands::browser_environment_cmd::archive_browser_environment_preset_cmd,
commands::browser_environment_cmd::restore_browser_environment_preset_cmd,
// Browser connector commands
commands::browser_connector_cmd::get_browser_connector_settings_cmd,
commands::browser_connector_cmd::set_browser_connector_install_root_cmd,
commands::browser_connector_cmd::set_browser_connector_enabled_cmd,
commands::browser_connector_cmd::set_system_connector_enabled_cmd,
commands::browser_connector_cmd::get_browser_connector_install_status_cmd,
commands::browser_connector_cmd::install_browser_connector_extension_cmd,
commands::browser_connector_cmd::open_browser_extensions_page_cmd,
// Browser profile commands
commands::browser_profile_cmd::list_browser_profiles_cmd,
commands::browser_profile_cmd::save_browser_profile_cmd,
@@ -0,0 +1,180 @@
//! 浏览器连接器命令
use crate::app::AppState;
use crate::services::browser_connector_service::{
get_browser_connector_install_status, get_browser_connector_settings,
install_browser_connector_extension, sync_browser_connector_auto_config_if_installed,
update_browser_connector_enabled, update_browser_connector_install_root,
update_system_connector_enabled, BrowserConnectorAutoConfig, BrowserConnectorInstallResult,
BrowserConnectorInstallStatus, BrowserConnectorSettingsSnapshot,
};
use serde::Deserialize;
use tauri::{AppHandle, State};
#[derive(Debug, Deserialize)]
pub struct SetBrowserConnectorInstallRootRequest {
pub install_root_dir: String,
}
#[derive(Debug, Deserialize)]
pub struct InstallBrowserConnectorExtensionRequest {
#[serde(default)]
pub install_root_dir: Option<String>,
#[serde(default)]
pub profile_key: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct SetSystemConnectorEnabledRequest {
pub id: String,
pub enabled: bool,
}
fn normalize_bridge_host(host: &str) -> String {
match host.trim() {
"" | "0.0.0.0" | "::" | "[::]" => "127.0.0.1".to_string(),
value => value.to_string(),
}
}
async fn build_auto_config(
app_state: &AppState,
profile_key: Option<&str>,
monitoring_enabled: bool,
) -> Result<BrowserConnectorAutoConfig, String> {
let state_guard = app_state.read().await;
let status = state_guard.status();
let host = normalize_bridge_host(&status.host);
let bridge_key = state_guard.config.server.api_key.clone();
let profile_key = profile_key
.map(str::trim)
.filter(|value| !value.is_empty())
.unwrap_or("default")
.to_string();
Ok(BrowserConnectorAutoConfig {
server_url: format!("ws://{host}:{}", status.port),
bridge_key,
profile_key,
monitoring_enabled,
})
}
#[tauri::command]
pub fn get_browser_connector_settings_cmd() -> Result<BrowserConnectorSettingsSnapshot, String> {
get_browser_connector_settings()
}
#[tauri::command]
pub fn set_browser_connector_install_root_cmd(
request: SetBrowserConnectorInstallRootRequest,
) -> Result<BrowserConnectorSettingsSnapshot, String> {
update_browser_connector_install_root(&request.install_root_dir)
}
#[tauri::command]
pub async fn set_browser_connector_enabled_cmd(
app_state: State<'_, AppState>,
enabled: bool,
) -> Result<BrowserConnectorSettingsSnapshot, String> {
let snapshot = update_browser_connector_enabled(enabled)?;
let auto_config = build_auto_config(app_state.inner(), None, enabled).await?;
let _ = sync_browser_connector_auto_config_if_installed(&auto_config)?;
Ok(snapshot)
}
#[tauri::command]
pub fn set_system_connector_enabled_cmd(
request: SetSystemConnectorEnabledRequest,
) -> Result<BrowserConnectorSettingsSnapshot, String> {
update_system_connector_enabled(&request.id, request.enabled)
}
#[tauri::command]
pub fn get_browser_connector_install_status_cmd(
app: AppHandle,
) -> Result<BrowserConnectorInstallStatus, String> {
get_browser_connector_install_status(&app)
}
#[tauri::command]
pub async fn install_browser_connector_extension_cmd(
app: AppHandle,
app_state: State<'_, AppState>,
request: InstallBrowserConnectorExtensionRequest,
) -> Result<BrowserConnectorInstallResult, String> {
let settings = if let Some(install_root_dir) = request.install_root_dir.as_deref() {
update_browser_connector_install_root(install_root_dir)?
} else {
get_browser_connector_settings()?
};
let install_root_dir = settings
.install_root_dir
.ok_or_else(|| "尚未选择浏览器连接器安装目录".to_string())?;
let auto_config = build_auto_config(
app_state.inner(),
request.profile_key.as_deref(),
settings.enabled,
)
.await?;
install_browser_connector_extension(&app, std::path::Path::new(&install_root_dir), &auto_config)
}
#[tauri::command]
pub async fn open_browser_extensions_page_cmd() -> Result<bool, String> {
#[cfg(target_os = "macos")]
{
let mut command = std::process::Command::new("open");
command.args(["-a", "Google Chrome", "chrome://extensions"]);
if let Err(primary_error) = command.spawn() {
std::process::Command::new("open")
.arg("chrome://extensions")
.spawn()
.map_err(|fallback_error| {
format!("打开 Chrome 扩展页面失败: {primary_error}; fallback: {fallback_error}")
})?;
}
}
#[cfg(target_os = "windows")]
{
std::process::Command::new("cmd")
.args(["/C", "start", "", "chrome://extensions"])
.spawn()
.map_err(|error| format!("打开 Chrome 扩展页面失败: {error}"))?;
}
#[cfg(target_os = "linux")]
{
let attempts = [
("google-chrome", vec!["chrome://extensions"]),
("chromium", vec!["chrome://extensions"]),
("xdg-open", vec!["chrome://extensions"]),
];
let mut last_error = None;
let mut opened = false;
for (binary, args) in attempts {
match std::process::Command::new(binary).args(args).spawn() {
Ok(_) => {
opened = true;
break;
}
Err(error) => {
last_error = Some(format!("{binary}: {error}"));
}
}
}
if !opened {
return Err(format!(
"打开 Chrome 扩展页面失败: {}",
last_error.unwrap_or_else(|| "没有可用的浏览器命令".to_string())
));
}
}
Ok(true)
}
+1
View File
@@ -5,6 +5,7 @@ pub mod asr_cmd;
pub mod aster_agent_cmd;
pub mod auto_fix_cmd;
pub mod automation_cmd;
pub mod browser_connector_cmd;
pub mod browser_environment_cmd;
pub mod browser_profile_cmd;
pub mod browser_runtime_cmd;
@@ -0,0 +1,523 @@
//! 浏览器连接器服务
//!
//! 负责浏览器连接器安装目录、导出同步和本地设置持久化。
use chrono::Utc;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use tauri::{AppHandle, Manager};
const SETTINGS_SUBDIR: &str = "connectors";
const SETTINGS_FILE_NAME: &str = "browser-connector-settings.json";
const EXTENSION_INSTALL_DIR_NAME: &str = "Lime Browser Connector";
const SYSTEM_CONNECTOR_DEFINITIONS: [(&str, &str, &str); 5] = [
(
"reminders",
"提醒事项",
"读取和管理你的提醒事项和任务列表。",
),
("calendar", "日历", "读取和管理你的日历事件。"),
("notes", "备忘录", "读取和创建你的备忘录。"),
("mail", "邮件", "读取邮件和创建草稿。"),
("contacts", "通讯录", "搜索、读取和创建联系人。"),
];
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BrowserConnectorAutoConfig {
#[serde(rename = "serverUrl")]
pub server_url: String,
#[serde(rename = "bridgeKey")]
pub bridge_key: String,
#[serde(rename = "profileKey")]
pub profile_key: String,
#[serde(rename = "monitoringEnabled")]
pub monitoring_enabled: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BrowserConnectorInstallStatus {
pub status: String,
pub install_root_dir: Option<String>,
pub install_dir: Option<String>,
pub bundled_name: String,
pub bundled_version: String,
pub installed_name: Option<String>,
pub installed_version: Option<String>,
pub message: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BrowserConnectorInstallResult {
pub install_root_dir: String,
pub install_dir: String,
pub bundled_name: String,
pub bundled_version: String,
pub installed_version: String,
pub auto_config_path: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemConnectorSnapshot {
pub id: String,
pub label: String,
pub description: String,
pub enabled: bool,
pub available: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BrowserConnectorSettingsSnapshot {
pub enabled: bool,
pub install_root_dir: Option<String>,
pub install_dir: Option<String>,
pub system_connectors: Vec<SystemConnectorSnapshot>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct BrowserConnectorSettingsRecord {
enabled: bool,
install_root_dir: Option<String>,
system_connectors: HashMap<String, bool>,
updated_at: String,
}
#[derive(Debug, Clone)]
struct ManifestInfo {
name: String,
version: String,
}
impl Default for BrowserConnectorSettingsRecord {
fn default() -> Self {
Self {
enabled: true,
install_root_dir: None,
system_connectors: default_system_connector_states(),
updated_at: Utc::now().to_rfc3339(),
}
}
}
fn default_system_connector_states() -> HashMap<String, bool> {
SYSTEM_CONNECTOR_DEFINITIONS
.iter()
.map(|(id, _, _)| ((*id).to_string(), false))
.collect()
}
fn browser_connector_settings_path() -> Result<PathBuf, String> {
Ok(lime_core::app_paths::preferred_data_dir()
.map_err(|error| format!("获取应用数据目录失败: {error}"))?
.join(SETTINGS_SUBDIR)
.join(SETTINGS_FILE_NAME))
}
fn ensure_parent_dir(path: &Path) -> Result<(), String> {
let Some(parent) = path.parent() else {
return Err("目标路径缺少父目录".to_string());
};
fs::create_dir_all(parent).map_err(|error| format!("创建目录失败: {error}"))
}
fn load_settings_record() -> Result<BrowserConnectorSettingsRecord, String> {
let path = browser_connector_settings_path()?;
if !path.exists() {
return Ok(BrowserConnectorSettingsRecord::default());
}
let content =
fs::read_to_string(&path).map_err(|error| format!("读取连接器设置失败: {error}"))?;
let mut record: BrowserConnectorSettingsRecord =
serde_json::from_str(&content).map_err(|error| format!("解析连接器设置失败: {error}"))?;
for (id, enabled) in default_system_connector_states() {
record.system_connectors.entry(id).or_insert(enabled);
}
Ok(record)
}
fn save_settings_record(record: &BrowserConnectorSettingsRecord) -> Result<(), String> {
let path = browser_connector_settings_path()?;
ensure_parent_dir(&path)?;
let content = serde_json::to_string_pretty(record)
.map_err(|error| format!("序列化连接器设置失败: {error}"))?;
fs::write(&path, content).map_err(|error| format!("写入连接器设置失败: {error}"))
}
fn path_to_string(path: &Path) -> String {
path.to_string_lossy().to_string()
}
fn normalize_install_root_dir(raw: &str) -> Result<PathBuf, String> {
let trimmed = raw.trim();
if trimmed.is_empty() {
return Err("安装目录不能为空".to_string());
}
let candidate = PathBuf::from(trimmed);
let normalized = if candidate.is_absolute() {
candidate
} else {
std::env::current_dir()
.map_err(|error| format!("获取当前目录失败: {error}"))?
.join(candidate)
};
Ok(normalized)
}
pub fn resolve_browser_connector_install_dir(root_dir: &Path) -> PathBuf {
root_dir.join(EXTENSION_INSTALL_DIR_NAME)
}
fn read_manifest_info(extension_dir: &Path) -> Result<ManifestInfo, String> {
let manifest_path = extension_dir.join("manifest.json");
let content = fs::read_to_string(&manifest_path)
.map_err(|error| format!("读取 manifest.json 失败 {:?}: {error}", manifest_path))?;
let manifest: Value = serde_json::from_str(&content)
.map_err(|error| format!("解析 manifest.json 失败: {error}"))?;
let name = manifest
.get("name")
.and_then(Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty())
.ok_or_else(|| "manifest.json 缺少 name".to_string())?
.to_string();
let version = manifest
.get("version")
.and_then(Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty())
.ok_or_else(|| "manifest.json 缺少 version".to_string())?
.to_string();
Ok(ManifestInfo { name, version })
}
fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<(), String> {
fs::create_dir_all(dst).map_err(|error| format!("创建目标目录失败: {error}"))?;
for entry in fs::read_dir(src).map_err(|error| format!("读取源目录失败: {error}"))? {
let entry = entry.map_err(|error| format!("读取目录项失败: {error}"))?;
let entry_path = entry.path();
let dst_path = dst.join(entry.file_name());
if entry_path.is_dir() {
copy_dir_recursive(&entry_path, &dst_path)?;
} else {
fs::copy(&entry_path, &dst_path)
.map_err(|error| format!("复制文件失败 {:?}: {error}", entry_path.file_name()))?;
}
}
Ok(())
}
pub fn resolve_browser_connector_extension_source(app: &AppHandle) -> Result<PathBuf, String> {
let path = if cfg!(debug_assertions) {
let current_dir =
std::env::current_dir().map_err(|error| format!("获取当前目录失败: {error}"))?;
let project_root = if current_dir.ends_with("src-tauri") {
current_dir
.parent()
.ok_or_else(|| "无法获取项目根目录".to_string())?
.to_path_buf()
} else {
current_dir
};
project_root.join("extensions").join("lime-chrome")
} else {
app.path()
.resource_dir()
.map_err(|error| format!("获取资源目录失败: {error}"))?
.join("extensions")
.join("lime-chrome")
};
if !path.exists() {
return Err(format!("未找到内置浏览器连接器目录: {:?}", path));
}
Ok(path)
}
fn build_install_status_from_paths(
bundled_extension_dir: &Path,
install_root_dir: Option<&Path>,
) -> Result<BrowserConnectorInstallStatus, String> {
let bundled_manifest = read_manifest_info(bundled_extension_dir)?;
let Some(root_dir) = install_root_dir else {
return Ok(BrowserConnectorInstallStatus {
status: "not_installed".to_string(),
install_root_dir: None,
install_dir: None,
bundled_name: bundled_manifest.name,
bundled_version: bundled_manifest.version,
installed_name: None,
installed_version: None,
message: Some("尚未选择浏览器连接器安装目录".to_string()),
});
};
let install_dir = resolve_browser_connector_install_dir(root_dir);
if !install_dir.exists() {
return Ok(BrowserConnectorInstallStatus {
status: "not_installed".to_string(),
install_root_dir: Some(path_to_string(root_dir)),
install_dir: Some(path_to_string(&install_dir)),
bundled_name: bundled_manifest.name,
bundled_version: bundled_manifest.version,
installed_name: None,
installed_version: None,
message: Some("已记录安装目录,但尚未导出浏览器连接器".to_string()),
});
}
match read_manifest_info(&install_dir) {
Ok(installed_manifest) => {
let status = if installed_manifest.version == bundled_manifest.version {
"installed"
} else {
"update_available"
};
let message = if status == "installed" {
Some("已安装最新版本浏览器连接器".to_string())
} else {
Some("检测到用户目录中的连接器版本落后于当前应用".to_string())
};
Ok(BrowserConnectorInstallStatus {
status: status.to_string(),
install_root_dir: Some(path_to_string(root_dir)),
install_dir: Some(path_to_string(&install_dir)),
bundled_name: bundled_manifest.name,
bundled_version: bundled_manifest.version,
installed_name: Some(installed_manifest.name),
installed_version: Some(installed_manifest.version),
message,
})
}
Err(error) => Ok(BrowserConnectorInstallStatus {
status: "broken".to_string(),
install_root_dir: Some(path_to_string(root_dir)),
install_dir: Some(path_to_string(&install_dir)),
bundled_name: bundled_manifest.name,
bundled_version: bundled_manifest.version,
installed_name: None,
installed_version: None,
message: Some(format!("安装目录存在,但扩展文件不完整: {error}")),
}),
}
}
pub fn get_browser_connector_install_status(
app: &AppHandle,
) -> Result<BrowserConnectorInstallStatus, String> {
let settings = load_settings_record()?;
let install_root_dir = settings
.install_root_dir
.as_deref()
.map(normalize_install_root_dir)
.transpose()?;
let bundled_extension_dir = resolve_browser_connector_extension_source(app)?;
build_install_status_from_paths(&bundled_extension_dir, install_root_dir.as_deref())
}
pub fn write_browser_connector_auto_config(
install_dir: &Path,
auto_config: &BrowserConnectorAutoConfig,
) -> Result<PathBuf, String> {
let auto_config_path = install_dir.join("auto_config.json");
let content = serde_json::to_string_pretty(auto_config)
.map_err(|error| format!("序列化 auto_config.json 失败: {error}"))?;
fs::write(&auto_config_path, content)
.map_err(|error| format!("写入 auto_config.json 失败 {:?}: {error}", auto_config_path))?;
Ok(auto_config_path)
}
pub fn install_browser_connector_extension(
app: &AppHandle,
install_root_dir: &Path,
auto_config: &BrowserConnectorAutoConfig,
) -> Result<BrowserConnectorInstallResult, String> {
let bundled_extension_dir = resolve_browser_connector_extension_source(app)?;
let bundled_manifest = read_manifest_info(&bundled_extension_dir)?;
let install_dir = resolve_browser_connector_install_dir(install_root_dir);
fs::create_dir_all(install_root_dir).map_err(|error| format!("创建安装根目录失败: {error}"))?;
if install_dir.exists() {
fs::remove_dir_all(&install_dir)
.map_err(|error| format!("删除旧连接器目录失败 {:?}: {error}", install_dir))?;
}
copy_dir_recursive(&bundled_extension_dir, &install_dir)?;
let auto_config_path = write_browser_connector_auto_config(&install_dir, auto_config)?;
Ok(BrowserConnectorInstallResult {
install_root_dir: path_to_string(install_root_dir),
install_dir: path_to_string(&install_dir),
bundled_name: bundled_manifest.name,
bundled_version: bundled_manifest.version.clone(),
installed_version: bundled_manifest.version,
auto_config_path: path_to_string(&auto_config_path),
})
}
pub fn sync_browser_connector_auto_config_if_installed(
auto_config: &BrowserConnectorAutoConfig,
) -> Result<Option<String>, String> {
let settings = load_settings_record()?;
let Some(root_dir) = settings.install_root_dir.as_deref() else {
return Ok(None);
};
let root_dir = normalize_install_root_dir(root_dir)?;
let install_dir = resolve_browser_connector_install_dir(&root_dir);
if !install_dir.exists() {
return Ok(None);
}
let auto_config_path = write_browser_connector_auto_config(&install_dir, auto_config)?;
Ok(Some(path_to_string(&auto_config_path)))
}
pub fn get_browser_connector_settings() -> Result<BrowserConnectorSettingsSnapshot, String> {
let record = load_settings_record()?;
Ok(build_settings_snapshot(&record))
}
fn build_settings_snapshot(
record: &BrowserConnectorSettingsRecord,
) -> BrowserConnectorSettingsSnapshot {
let install_root_dir = record.install_root_dir.clone();
let install_dir = install_root_dir
.as_deref()
.map(|raw| normalize_install_root_dir(raw).unwrap_or_else(|_| PathBuf::from(raw)))
.map(|root_dir| path_to_string(&resolve_browser_connector_install_dir(&root_dir)));
BrowserConnectorSettingsSnapshot {
enabled: record.enabled,
install_root_dir,
install_dir,
system_connectors: SYSTEM_CONNECTOR_DEFINITIONS
.iter()
.map(|(id, label, description)| SystemConnectorSnapshot {
id: (*id).to_string(),
label: (*label).to_string(),
description: (*description).to_string(),
enabled: record.system_connectors.get(*id).copied().unwrap_or(false),
available: cfg!(target_os = "macos"),
})
.collect(),
}
}
pub fn update_browser_connector_install_root(
install_root_dir: &str,
) -> Result<BrowserConnectorSettingsSnapshot, String> {
let normalized = normalize_install_root_dir(install_root_dir)?;
fs::create_dir_all(&normalized).map_err(|error| format!("创建安装目录失败: {error}"))?;
let mut record = load_settings_record()?;
record.install_root_dir = Some(path_to_string(&normalized));
record.updated_at = Utc::now().to_rfc3339();
save_settings_record(&record)?;
Ok(build_settings_snapshot(&record))
}
pub fn update_browser_connector_enabled(
enabled: bool,
) -> Result<BrowserConnectorSettingsSnapshot, String> {
let mut record = load_settings_record()?;
record.enabled = enabled;
record.updated_at = Utc::now().to_rfc3339();
save_settings_record(&record)?;
Ok(build_settings_snapshot(&record))
}
pub fn update_system_connector_enabled(
id: &str,
enabled: bool,
) -> Result<BrowserConnectorSettingsSnapshot, String> {
if !SYSTEM_CONNECTOR_DEFINITIONS
.iter()
.any(|(entry_id, _, _)| *entry_id == id)
{
return Err(format!("未知的系统连接器: {id}"));
}
let mut record = load_settings_record()?;
record.system_connectors.insert(id.to_string(), enabled);
record.updated_at = Utc::now().to_rfc3339();
save_settings_record(&record)?;
Ok(build_settings_snapshot(&record))
}
#[cfg(test)]
mod tests {
use super::*;
fn create_extension_dir(base_dir: &Path, version: &str) -> PathBuf {
let extension_dir = base_dir.join(format!("extension-{version}"));
fs::create_dir_all(&extension_dir).expect("创建扩展目录失败");
fs::write(
extension_dir.join("manifest.json"),
format!(
r#"{{
"name": "Lime Browser Connector",
"version": "{version}"
}}"#
),
)
.expect("写入 manifest 失败");
extension_dir
}
#[test]
fn install_dir_should_use_fixed_child_directory() {
let root = PathBuf::from("/tmp/lime-browser-root");
let install_dir = resolve_browser_connector_install_dir(&root);
assert_eq!(install_dir, root.join("Lime Browser Connector"));
}
#[test]
fn status_should_report_not_installed_without_root_dir() {
let temp_dir = tempfile::tempdir().expect("创建临时目录失败");
let bundled_dir = create_extension_dir(temp_dir.path(), "1.0.0");
let status = build_install_status_from_paths(&bundled_dir, None).expect("读取状态失败");
assert_eq!(status.status, "not_installed");
assert_eq!(status.bundled_version, "1.0.0");
assert!(status.install_root_dir.is_none());
}
#[test]
fn status_should_report_update_available_when_versions_differ() {
let temp_dir = tempfile::tempdir().expect("创建临时目录失败");
let bundled_dir = create_extension_dir(temp_dir.path(), "1.1.0");
let install_root = temp_dir.path().join("user-selected");
let installed_dir = resolve_browser_connector_install_dir(&install_root);
fs::create_dir_all(&installed_dir).expect("创建安装目录失败");
fs::write(
installed_dir.join("manifest.json"),
r#"{
"name": "Lime Browser Connector",
"version": "1.0.0"
}"#,
)
.expect("写入已安装 manifest 失败");
let status = build_install_status_from_paths(&bundled_dir, Some(&install_root))
.expect("读取状态失败");
assert_eq!(status.status, "update_available");
assert_eq!(status.installed_version.as_deref(), Some("1.0.0"));
assert_eq!(status.bundled_version, "1.1.0");
}
}
+1
View File
@@ -14,6 +14,7 @@ pub mod artifact_prompt_service;
pub mod artifact_request_metadata_service;
pub mod auto_memory_service;
pub mod automation_service;
pub mod browser_connector_service;
pub mod browser_environment_service;
pub mod browser_profile_service;
pub mod browser_runtime_window;
@@ -709,7 +709,8 @@ fn format_observability_signal_list(observability_summary: &Value, status: &str)
values
.iter()
.filter(|value| {
value.get("status")
value
.get("status")
.and_then(Value::as_str)
.map(|value| value == status)
.unwrap_or(false)