feat: v1.0 — am registry : registre communautaire de catalogues (publication + recherche sur Gitea), manifeste source/version/auteur/sha256, installation avec validation du checksum et décision de confiance explicite (closes #77)

- src/registry.rs : Manifest (schema/name/version/author/source/agents_count/sha256), publish (écrit am-manifest.json + instructions git), search (multi-sources via settings.registry.sources), install (manifeste → catalogue → sha256 → source cohérente → confiance → register_include), list
- settings.registry {sources, author} + doc config.yaml ; CLI/REPL/help/man/i18n
- 11 tests : hash, manifest_url, publish (fichier + refus catalogue vide/sans source), E2E install tiny_http (refus sans confiance, enregistrement avec --yes, 4 requêtes), source mismatch
This commit is contained in:
2026-08-19 22:23:32 -04:00
parent 12eb09b01a
commit 900f70c249
13 changed files with 683 additions and 2 deletions
+34 -1
View File
@@ -110,6 +110,7 @@ const COMMAND_DESCRIPTIONS: &[(&str, &str)] = &[
("man", "readable man page in the terminal"),
("models", "list local models (ollama, llama.cpp, LM Studio)"),
("catalog", "manage remote catalogs"),
("registry", "publish, search and install agent catalogs (Gitea)"),
("providers", "manage the LLM provider registry (base URLs, models, default)"),
("suggest", "recommend an agent for a request"),
("audit", "who changed what, when (config checksums)"),
@@ -236,7 +237,7 @@ impl AmCompleter {
"start", "stop", "restart", "run", "doctor", "config", "completion",
"self-update", "self-uninstall", "export", "import", "shell", "theme", "lang",
"tip", "dashboard", "favorite", "unfavorite", "note", "tag", "untag", "tags",
"profile", "man", "models", "catalog", "providers", "suggest", "ask", "audit",
"profile", "man", "models", "catalog", "providers", "suggest", "ask", "registry", "audit",
"service", "schedule", "monitor", "web", "sync", "migrate", "playbook", "lab", "plugins",
"ls", "dir", "cd", "ps", "where", "get", "help", "version", "exit",
],
@@ -778,6 +779,7 @@ pub fn banner_box(
"Commands".to_string()
}));
rows.push(inner(" catalog list · status · search · info · init".to_string()));
rows.push(inner(" registry publish · search · install · list".to_string()));
rows.push(inner(
" activity sessions · stats · top · report · projects · timeline · log · logs · history"
.to_string(),
@@ -1543,6 +1545,37 @@ fn handle_line(
return Ok(false);
}
},
"registry" => match rest.first().map(|s| s.as_str()) {
None => Command::Registry(crate::cli::RegistryArgs::default()),
Some("list") => Command::Registry(crate::cli::RegistryArgs {
sub: Some(crate::cli::RegistryCmd::List),
}),
Some("search") if rest.len() >= 2 => Command::Registry(crate::cli::RegistryArgs {
sub: Some(crate::cli::RegistryCmd::Search {
query: rest[1..].join(" "),
}),
}),
Some("install") if rest.len() >= 2 => Command::Registry(crate::cli::RegistryArgs {
sub: Some(crate::cli::RegistryCmd::Install {
url: rest[1].clone(),
yes: flag("--yes"),
}),
}),
Some("publish") if rest.len() >= 2 => Command::Registry(crate::cli::RegistryArgs {
sub: Some(crate::cli::RegistryCmd::Publish {
catalog: rest[1].clone().into(),
source: opt_value("--source"),
author: opt_value("--author"),
version: opt_value("--version"),
}),
}),
_ => {
app.log.error(crate::i18n::tr(
"usage: registry publish <catalogue.yaml> --source <url> | registry search <mot> | registry install <url> | registry list",
));
return Ok(false);
}
},
"suggest" => {
if rest.is_empty() {
app.log.error(crate::i18n::tr("usage: suggest <requête> — ex: suggest un agent pour du Python"));