systemprompt_cli/commands/plugins/
show.rs1use anyhow::{Result, anyhow};
8use clap::Args;
9use systemprompt_extension::{Extension, ExtensionRegistry};
10use systemprompt_loader::ExtensionLoader;
11
12use super::types::{
13 ExtensionDetailOutput, ExtensionSource, JobInfo, RoleInfo, SchemaInfo, TemplateInfo, ToolInfo,
14};
15use crate::CliConfig;
16use crate::shared::CommandOutput;
17
18#[derive(Debug, Clone, Args)]
19pub struct ShowArgs {
20 #[arg(help = "Extension ID to show")]
21 pub id: String,
22}
23
24pub fn execute(args: &ShowArgs, _config: &CliConfig) -> Result<CommandOutput> {
25 let registry = ExtensionRegistry::discover()?;
26 let needle = args.id.to_lowercase();
27
28 let ext = registry.get(&args.id).or_else(|| {
29 registry
30 .extensions()
31 .iter()
32 .find(|e| e.id().to_lowercase() == needle || e.name().to_lowercase() == needle)
33 });
34
35 let Some(ext) = ext else {
36 return show_manifest(&args.id)
37 .ok_or_else(|| anyhow!("Extension '{}' not found", args.id))?;
38 };
39
40 let output = build_detail_output(ext.as_ref());
41
42 Ok(CommandOutput::card_value(
43 format!("Extension: {}", args.id),
44 &output,
45 ))
46}
47
48pub fn build_detail_output(ext: &dyn Extension) -> ExtensionDetailOutput {
49 ExtensionDetailOutput {
50 id: systemprompt_identifiers::PluginId::new(ext.id()),
51 name: ext.name().to_owned(),
52 version: ext.version().to_owned(),
53 priority: ext.priority(),
54 source: ExtensionSource::Compiled,
55 dependencies: ext.dependencies().iter().map(|s| (*s).to_owned()).collect(),
56 config_prefix: ext.config_prefix().map(String::from),
57 jobs: job_infos(ext),
58 templates: template_infos(ext),
59 schemas: schema_infos(ext),
60 routes: vec![],
61 tools: ext
62 .tool_providers()
63 .iter()
64 .map(|_provider| ToolInfo {
65 name: "tool_provider".to_owned(),
66 })
67 .collect(),
68 roles: role_infos(ext),
69 storage_paths: ext
70 .required_storage_paths()
71 .iter()
72 .map(|s| (*s).to_owned())
73 .collect(),
74 }
75}
76
77fn job_infos(ext: &dyn Extension) -> Vec<JobInfo> {
78 ext.jobs()
79 .iter()
80 .map(|job| JobInfo {
81 name: job.name().to_owned(),
82 schedule: job.schedule().to_owned(),
83 enabled: job.enabled(),
84 })
85 .collect()
86}
87
88fn template_infos(ext: &dyn Extension) -> Vec<TemplateInfo> {
89 ext.template_providers()
90 .iter()
91 .flat_map(|provider| {
92 provider
93 .templates()
94 .iter()
95 .map(|t| TemplateInfo {
96 name: t.name.clone(),
97 description: t.content_types.join(", "),
98 })
99 .collect::<Vec<_>>()
100 })
101 .collect()
102}
103
104fn schema_infos(ext: &dyn Extension) -> Vec<SchemaInfo> {
105 ext.schemas()
106 .iter()
107 .map(|schema| SchemaInfo {
108 table: schema.table.clone(),
109 source: "inline".to_owned(),
110 required_columns: schema.required_columns.clone(),
111 })
112 .collect()
113}
114
115fn role_infos(ext: &dyn Extension) -> Vec<RoleInfo> {
116 ext.roles()
117 .iter()
118 .map(|role| RoleInfo {
119 name: role.name.clone(),
120 display_name: role.display_name.clone(),
121 description: role.description.clone(),
122 permissions: role.permissions.clone(),
123 })
124 .collect()
125}
126
127fn show_manifest(id: &str) -> Option<Result<CommandOutput>> {
128 let project_root = std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::new());
129 let needle = id.to_lowercase();
130 let ext = ExtensionLoader::discover(&project_root)
131 .into_iter()
132 .find(|e| e.manifest.extension.name.to_lowercase() == needle)?;
133
134 let name = ext.manifest.extension.name;
135 let output = ExtensionDetailOutput {
136 id: systemprompt_identifiers::PluginId::new(name.clone()),
137 name: name.clone(),
138 version: "manifest".to_owned(),
139 priority: 100,
140 source: ExtensionSource::Manifest,
141 dependencies: vec![],
142 config_prefix: None,
143 jobs: vec![],
144 templates: vec![],
145 schemas: vec![],
146 routes: vec![],
147 tools: vec![],
148 roles: vec![],
149 storage_paths: vec![],
150 };
151
152 Some(Ok(CommandOutput::card_value(
153 format!("Extension: {}", name),
154 &output,
155 )))
156}