1use qualia_core_db::resource_catalog::{self, ResourceCatalog};
25use std::path::{Path, PathBuf};
26
27pub fn load_catalog(catalog_dir: &Path) -> Result<ResourceCatalog, String> {
29 resource_catalog::load_from_dir(catalog_dir).map_err(|e| e.to_string())
30}
31
32pub async fn handle(subcommand: &str, arg: Option<&str>) {
35 let catalog_dir = resource_catalog::resolve_resources_dir();
36 let catalog = match load_catalog(&catalog_dir) {
37 Ok(c) => c,
38 Err(e) => {
39 eprintln!("Error loading catalog: {}", e);
40 return;
41 }
42 };
43
44 match subcommand {
45 "list" => cmd_list(&catalog, arg),
46 "show" => cmd_show(&catalog, arg),
47 "download" => {
48 if let Some(id) = arg {
49 cmd_download(&catalog, id).await;
50 } else {
51 eprintln!("Usage: qualia resources download <llm-id>");
52 }
53 }
54 "import-ontology" => {
55 if let Some(id) = arg {
56 cmd_import_ontology(&catalog, id).await;
57 } else {
58 eprintln!("Usage: qualia resources import-ontology <ontology-id>");
59 }
60 }
61 _ => print_help(),
62 }
63}
64
65fn cmd_list(catalog: &ResourceCatalog, filter: Option<&str>) {
68 match filter.unwrap_or("all") {
69 "llms" | "llm" => list_llms(catalog),
70 "ontologies" | "ont" => list_ontologies(catalog),
71 "sparql" => list_sparql(catalog),
72 _ => {
73 list_llms(catalog);
74 list_ontologies(catalog);
75 list_sparql(catalog);
76 }
77 }
78}
79
80fn list_llms(catalog: &ResourceCatalog) {
81 println!("\nLLMs ({}):", catalog.llms.len());
82 for m in &catalog.llms {
83 println!(
84 " {:40} {:8} {:7} {}MB",
85 m.id,
86 m.format,
87 m.quantization.as_deref().unwrap_or("—"),
88 m.size_mb.unwrap_or(0)
89 );
90 }
91}
92
93fn list_ontologies(catalog: &ResourceCatalog) {
94 println!("\nOntologies ({}):", catalog.ontologies.len());
95 for o in &catalog.ontologies {
96 println!(
97 " {:40} {:6} ~{}MB {}",
98 o.id,
99 o.format,
100 o.size_estimate_mb.unwrap_or(0.0),
101 o.domain.as_deref().unwrap_or("—")
102 );
103 }
104}
105
106fn list_sparql(catalog: &ResourceCatalog) {
107 println!("\nSPARQL endpoints ({}):", catalog.sparql_endpoints.len());
108 for s in &catalog.sparql_endpoints {
109 println!(" {:40} {}", s.id, s.endpoint);
110 }
111}
112
113fn cmd_show(catalog: &ResourceCatalog, id: Option<&str>) {
116 let id = match id {
117 Some(i) => i,
118 None => {
119 eprintln!("Usage: qualia resources show <id>");
120 return;
121 }
122 };
123
124 if let Some(m) = catalog.find_llm(id) {
125 println!("LLM: {}", m.name);
126 println!(" id : {}", m.id);
127 println!(" format : {}", m.format);
128 println!(
129 " quantization : {}",
130 m.quantization.as_deref().unwrap_or("—")
131 );
132 println!(" size : {}MB", m.size_mb.unwrap_or(0));
133 println!(" RAM estimate : {}MB", m.ram_estimate_mb.unwrap_or(0));
134 println!(" license : {}", m.license.as_deref().unwrap_or("—"));
135 if let Some(url) = m.download.resolved_url() {
136 println!(" download url : {}", url);
137 }
138 if let Some(ref notes) = m.notes {
139 println!(" notes : {}", notes);
140 }
141 println!(
142 " profile_id : 0x{:016x} (q_hash(\"profile:{}\"))",
143 qualia_core_db::q_hash(&format!("profile:{}", m.id)),
144 m.id
145 );
146 return;
147 }
148
149 if let Some(o) = catalog.find_ontology(id) {
150 println!("Ontology: {}", o.name);
151 println!(" id : {}", o.id);
152 println!(" format : {}", o.format);
153 println!(" domain : {}", o.domain.as_deref().unwrap_or("—"));
154 println!(" size : ~{}MB", o.size_estimate_mb.unwrap_or(0.0));
155 println!(" license : {}", o.license.as_deref().unwrap_or("—"));
156 if let Some(url) = o.download.resolved_url() {
157 println!(" download : {}", url);
158 }
159 return;
160 }
161
162 if let Some(s) = catalog.find_sparql(id) {
163 println!("SPARQL: {}", s.name);
164 println!(" endpoint : {}", s.endpoint);
165 println!(
166 " maintainer : {}",
167 s.maintainer.as_deref().unwrap_or("—")
168 );
169 println!(
170 " reliability : {}",
171 s.reliability.as_deref().unwrap_or("—")
172 );
173 return;
174 }
175
176 eprintln!("Not found: {}", id);
177}
178
179fn default_storage_root() -> PathBuf {
182 std::env::var("QUALIA_STORAGE")
183 .map(PathBuf::from)
184 .unwrap_or_else(|_| {
185 std::env::var("HOME")
186 .or_else(|_| std::env::var("USERPROFILE"))
187 .map(PathBuf::from)
188 .unwrap_or_else(|_| PathBuf::from("."))
189 .join(".qualia")
190 })
191}
192
193async fn cmd_download(catalog: &ResourceCatalog, id: &str) {
194 let storage = default_storage_root();
195
196 match qualia_client_core::model_lifecycle::install_catalog_llm(catalog, id, &storage).await {
197 Ok(result) => {
198 println!("Install complete: {}", result.gguf_path);
199 println!(" profile_id : 0x{:016x}", result.profile_id);
200 println!(" pointers : {}", result.pointer_quin_count);
201 println!(" WAL : {}", result.wal_path);
202 println!(" lifecycle : {}", result.lifecycle_state);
203 println!(
204 "\nActivate in LLM Hub, then chat with profile_id 0x{:016x}",
205 result.profile_id
206 );
207 }
208 Err(e) => eprintln!("Download failed: {e}"),
209 }
210}
211
212async fn cmd_import_ontology(catalog: &ResourceCatalog, id: &str) {
215 let storage = default_storage_root();
216
217 match qualia_client_core::resource_import::import_catalog_ontology(catalog, id, &storage).await
218 {
219 Ok(result) => {
220 println!("Import complete: {}", result.q42_path);
221 println!(" Quins: {}", result.quin_count);
222 println!(" WAL: {}", result.wal_path);
223 println!(" SHA256: {}", result.sha256);
224 }
225 Err(e) => eprintln!("Import failed: {e}"),
226 }
227}
228
229fn print_help() {
230 println!("qualia resources <subcommand> [arg]");
231 println!(" list [llms|ontologies|sparql] List catalog entries");
232 println!(" show <id> Show details for a resource");
233 println!(" download <llm-id> Download GGUF → WAL + CapabilityProfile");
234 println!(" import-ontology <ont-id> Download + ingest ontology → .q42 + WAL");
235}