Phase 1 — Critical Safety: - #11: bounded IPC reads via read_bounded_line (server + client) - #13: sound Send+Sync via Arc<Mutex<RuntimeHandle>>; remove unsafe impl Sync - #10: ItemSource enum (Core/NativePlugin/ScriptPlugin) on LaunchItem; script plugin allowlist guard in launch_item() Phase 2 — Config System Overhaul: - #6: remove dead enabled_plugins field - #1: replace #[serde(flatten)] with explicit Config::plugin_config - #4: Server.config Arc<RwLock<Config>>; ConfigProvider shares same Arc - #2/#3: atomic config save (temp+rename); TOCTOU fixed — write lock held across mutation and save() in config_editor - #23: fs2 lock_exclusive() on .lock sidecar file in Config::save() - #16: SIGHUP handler reloads config; ExecReload in systemd service Phase 3 — Plugin Architecture: - #7: HostAPI v4 with get_config_string/int/bool; PLUGIN_CONFIG OnceLock in native_loader, set_shared_config() called from Server::bind() - #5: PluginEntry + Request::PluginList + Response::PluginList; plugin_registry in ProviderManager tracks active and suppressed native plugins; cmd_list_installed shows both script and native plugins - #9: suppressed native plugin log level info! → warn! - #8: ProviderType doc glossary; plugins/mod.rs terminology table Phase 4 — Data Integrity: - #12: all into_inner() in server.rs + providers/mod.rs → explicit Response::Error; watcher exits on poisoned lock - #14: FrecencyStore::prune() (180-day age + 5000-entry cap) called on load - #17: empty command guard in launch_item(); warn in lua_provider - #24: 5-min periodic frecency save thread; SIGTERM/SIGINT saves frecency before exit (replaces ctrlc handler) Phase 5 — UI & UX: - #19: provider_meta.rs ProviderMeta + meta_for(); three match blocks collapsed - #18: desktop file dedup via seen_basenames HashSet in ApplicationProvider - #20: search_filtered gains tag_filter param; non-frecency path now filters - #15: widget refresh 5s→10s; skip when user is typing Phase 6 — Hardening: - #22: catch_unwind removed from reload_runtimes(); direct drop() - #21: AtomicUsize + RAII ConnectionGuard; MAX_CONNECTIONS = 16 Deps: add fs2 = "0.4"; remove ctrlc and toml_edit from owlry-core
94 lines
2.7 KiB
Rust
94 lines
2.7 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
#[serde(tag = "type", rename_all = "snake_case")]
|
|
pub enum Request {
|
|
Query {
|
|
text: String,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
modes: Option<Vec<String>>,
|
|
},
|
|
Launch {
|
|
item_id: String,
|
|
provider: String,
|
|
},
|
|
Providers,
|
|
Refresh {
|
|
provider: String,
|
|
},
|
|
Toggle,
|
|
Submenu {
|
|
plugin_id: String,
|
|
data: String,
|
|
},
|
|
PluginAction {
|
|
command: String,
|
|
},
|
|
/// Query the daemon's plugin registry (native plugins + suppressed entries).
|
|
PluginList,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
#[serde(tag = "type", rename_all = "snake_case")]
|
|
pub enum Response {
|
|
Results { items: Vec<ResultItem> },
|
|
Providers { list: Vec<ProviderDesc> },
|
|
SubmenuItems { items: Vec<ResultItem> },
|
|
PluginList { entries: Vec<PluginEntry> },
|
|
Ack,
|
|
Error { message: String },
|
|
}
|
|
|
|
/// Registry entry for a loaded or suppressed plugin (native plugins only).
|
|
/// Script plugins are tracked separately via filesystem discovery.
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct PluginEntry {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub version: String,
|
|
/// Plugin runtime type: "native", "builtin"
|
|
pub runtime: String,
|
|
/// Load status: "active" or "suppressed"
|
|
pub status: String,
|
|
/// Human-readable detail for non-active status (e.g. suppression reason)
|
|
#[serde(default, skip_serializing_if = "String::is_empty")]
|
|
pub status_detail: String,
|
|
/// Provider type IDs registered by this plugin
|
|
#[serde(default)]
|
|
pub providers: Vec<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct ResultItem {
|
|
pub id: String,
|
|
pub title: String,
|
|
pub description: String,
|
|
pub icon: String,
|
|
pub provider: String,
|
|
pub score: i64,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub command: Option<String>,
|
|
#[serde(default)]
|
|
pub terminal: bool,
|
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
pub tags: Vec<String>,
|
|
/// Item trust level: "core", "native_plugin", or "script_plugin".
|
|
/// Defaults to "core" when absent (backwards-compatible with old daemons).
|
|
#[serde(default = "default_source")]
|
|
pub source: String,
|
|
}
|
|
|
|
fn default_source() -> String {
|
|
"core".to_string()
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct ProviderDesc {
|
|
pub id: String,
|
|
pub name: String,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub prefix: Option<String>,
|
|
pub icon: String,
|
|
pub position: String,
|
|
}
|