fix: détection externe — champ detect (sonde PATH explicite) pour les lanceurs interpréteurs
lazycodex (run: npx lazycodex-ai) et nanobot (run: python -m nanobot) étaient déclarés « external » dès que npx/python étaient sur le PATH : la sonde utilisait le premier mot de run au lieu du vrai binaire. - AgentDef.detect : sonde PATH explicite, repli sur first_token sinon - probe.rs : cache invalidé quand les tokens du catalogue changent (token_hash), plus besoin d'attendre un changement de PATH - config.yaml : detect: lazycodex-ai / detect: nanobot - tests : detect_token_prefers_explicit_binary + 432 tests verts - v1.0.3, man pages régénérées
This commit is contained in:
@@ -398,6 +398,13 @@ pub struct AgentDef {
|
||||
/// Command used to start the agent (e.g. "claude", "python -m nanobot").
|
||||
#[serde(default)]
|
||||
pub run: Option<String>,
|
||||
/// Binary name probed on the PATH for external detection. When unset,
|
||||
/// the first token of `run` is used — which is wrong for interpreter
|
||||
/// launchers ("npx pkg", "python -m pkg"): the interpreter itself would
|
||||
/// be matched, reporting the agent as present whenever node/python is
|
||||
/// on the PATH. Set it to the real entry point (e.g. "lazycodex-ai").
|
||||
#[serde(default)]
|
||||
pub detect: Option<String>,
|
||||
/// Default arguments appended to the run command.
|
||||
#[serde(default)]
|
||||
pub args: Vec<String>,
|
||||
@@ -516,6 +523,12 @@ impl AgentDef {
|
||||
})
|
||||
}
|
||||
|
||||
/// Binary probed on the PATH for external detection: the explicit
|
||||
/// `detect` name when set, otherwise the first token of `run`.
|
||||
pub fn detect_token(&self) -> Option<String> {
|
||||
self.detect.clone().or_else(|| self.first_token())
|
||||
}
|
||||
|
||||
/// The run command split into tokens (empty when unset).
|
||||
pub fn run_tokens(&self) -> Vec<String> {
|
||||
self.run
|
||||
@@ -1167,6 +1180,31 @@ mod tests {
|
||||
assert!(validate(&cfg).is_empty(), "embedded config invalid: {:?}", validate(&cfg));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detect_token_prefers_explicit_binary() {
|
||||
// Interpreter launcher: without `detect`, the interpreter itself
|
||||
// would be probed (false "external" when node/python is on PATH).
|
||||
let a: AgentDef = serde_yaml::from_str(
|
||||
"name: lazycodex\nrun: \"npx lazycodex-ai\"\ndetect: lazycodex-ai\n",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(a.detect_token().as_deref(), Some("lazycodex-ai"));
|
||||
// No `detect`: falls back to the first token of run.
|
||||
let b: AgentDef =
|
||||
serde_yaml::from_str("name: nanobot\nrun: \"python -m nanobot\"\n").unwrap();
|
||||
assert_eq!(b.detect_token().as_deref(), Some("python"));
|
||||
assert_eq!(b.first_token().as_deref(), Some("python"));
|
||||
// Plain binary command is unchanged.
|
||||
let c: AgentDef = serde_yaml::from_str("name: claude\nrun: claude\n").unwrap();
|
||||
assert_eq!(c.detect_token().as_deref(), Some("claude"));
|
||||
// The embedded catalog must stay valid with the new field.
|
||||
let cfg: Config = serde_yaml::from_str(DEFAULT_CONFIG).unwrap();
|
||||
let lazy = cfg.agents.iter().find(|a| a.name == "lazycodex").unwrap();
|
||||
assert_eq!(lazy.detect_token().as_deref(), Some("lazycodex-ai"));
|
||||
let nano = cfg.agents.iter().find(|a| a.name == "nanobot").unwrap();
|
||||
assert_eq!(nano.detect_token().as_deref(), Some("nanobot"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expands_paths() {
|
||||
std::env::set_var("AM_TEST_EXPAND_VAR", "expanded-value");
|
||||
|
||||
Reference in New Issue
Block a user