mirror of
https://github.com/storytold/craft-anything.git
synced 2026-11-05 20:47:45 +01:00
Refuse a folder or app bundle as an agent's program
The "Where is the program?" picker returned Antigravity.app on a Mac, and the path was saved as the agent's command (#467). set_command now refuses a folder for a local agent, with a message that names the command-line program to pick. A folder saved before that is not started: the usual program name is used, as when none is chosen. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -64,6 +64,11 @@ pub fn program_of(config: &BackendConfig, options: &BackendOptions) -> String {
|
||||
let usual = program_for(&config.model);
|
||||
let Some(chosen) = options.command.as_deref().map(str::trim).filter(|c| !c.is_empty()) else { return usual };
|
||||
let local = config.ssh_host.as_deref().is_none_or(|h| h.trim().is_empty());
|
||||
// A folder or app bundle saved before set_command refused it (#467) can't be started: the usual
|
||||
// program is used, as when none is chosen.
|
||||
if local && Path::new(chosen).is_dir() {
|
||||
return usual;
|
||||
}
|
||||
let path = Path::new(chosen);
|
||||
if !local || path.parent().is_none_or(|p| p.as_os_str().is_empty()) || path.exists() {
|
||||
return chosen.to_string();
|
||||
@@ -1118,6 +1123,12 @@ mod tests {
|
||||
assert_eq!(program_of(&config(None), &chosen(&new)), new.display().to_string(), "a path that's there is used as it is");
|
||||
assert_eq!(program_of(&config(Some("box")), &chosen(&gone)), gone.display().to_string(), "another computer's path isn't checked here");
|
||||
assert_eq!(program_of(&config(None), &BackendOptions::default()), "codex", "nothing chosen: the usual name");
|
||||
// #467: a saved app bundle is never started; the usual name is looked up instead.
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let bundle = dir.path().join("Codex.app");
|
||||
std::fs::create_dir_all(&bundle).unwrap();
|
||||
assert_eq!(program_of(&config(None), &chosen(&bundle)), "codex", "an app bundle is not the program");
|
||||
assert_eq!(program_of(&config(Some("box")), &chosen(&bundle)), bundle.display().to_string(), "another computer's path isn't checked here");
|
||||
let bare = BackendOptions { command: Some("my-codex".into()), ..Default::default() };
|
||||
assert_eq!(program_of(&config(None), &bare), "my-codex", "a bare name is left to PATH");
|
||||
}
|
||||
|
||||
+12
-1
@@ -522,9 +522,20 @@ impl AiHub {
|
||||
/// The program an agent app runs (`None` or empty: found by its usual name, on PATH and in the
|
||||
/// usual install folders). Returns false when there was no such backend.
|
||||
pub fn set_command(&self, name: &str, command: Option<&str>) -> Result<bool, ToolError> {
|
||||
let command = command.map(str::trim).filter(|c| !c.is_empty());
|
||||
let mut cfg = self.config_to_change()?;
|
||||
let Some(b) = cfg.backends.iter_mut().find(|b| b.config.name == name) else { return Ok(false) };
|
||||
b.options.command = command.map(str::trim).filter(|c| !c.is_empty()).map(str::to_string);
|
||||
// A folder can't be started, and on a Mac an app is one (#467: the file picker returned
|
||||
// `Antigravity.app`). Another computer's path isn't looked at here.
|
||||
let local = b.config.ssh_host.as_deref().is_none_or(|h| h.trim().is_empty());
|
||||
if let Some(c) = command
|
||||
&& local
|
||||
&& std::path::Path::new(c).is_dir()
|
||||
{
|
||||
return Err(ToolError::invalid(format!("{c} is a folder, so it can't be the agent's program. On a Mac an app is a folder."))
|
||||
.with_remediation("Pick the agent's command-line program instead, such as `agy`, or clear the program to use the usual one."));
|
||||
}
|
||||
b.options.command = command.map(str::to_string);
|
||||
self.save_config(&cfg)?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
@@ -304,6 +304,27 @@ fn an_agent_apps_program_is_set_and_cleared() {
|
||||
assert_eq!(program(&h), None, "blank finds it by its usual name again");
|
||||
}
|
||||
|
||||
/// #467: a folder or app bundle can't be an agent's program (the file picker returns
|
||||
/// `Antigravity.app` on a Mac); it's refused and the saved program stays as it was.
|
||||
#[test]
|
||||
fn an_app_bundle_is_refused_as_an_agents_program() {
|
||||
let (h, d) = hub();
|
||||
add(&h, "OpenCode", BackendKind::AgentCli, "http://127.0.0.1:9", None);
|
||||
let program = |h: &AiHub| h.options().get("OpenCode").and_then(|o| o.command.clone());
|
||||
let bundle = d.path().join("Antigravity.app");
|
||||
std::fs::create_dir_all(bundle.join("Contents")).unwrap();
|
||||
let err = h.set_command("OpenCode", Some(&bundle.display().to_string())).unwrap_err();
|
||||
assert_eq!(err.code, ErrorCode::BadInput, "{err:?}");
|
||||
assert!(err.message.contains("is a folder"), "{}", err.message);
|
||||
assert_eq!(program(&h), None, "nothing is saved for a refused folder");
|
||||
// A file, and a bare name, are still accepted.
|
||||
let file = d.path().join("agy");
|
||||
std::fs::write(&file, "#!/bin/sh\n").unwrap();
|
||||
assert!(h.set_command("OpenCode", Some(&file.display().to_string())).unwrap());
|
||||
assert!(h.set_command("OpenCode", Some("agy")).unwrap());
|
||||
assert_eq!(program(&h).as_deref(), Some("agy"));
|
||||
}
|
||||
|
||||
/// Claude Code's folder (`CLAUDE_CONFIG_DIR`) is chosen per AI, so two can be two accounts; only
|
||||
/// a full path, and only for an agent app that keeps one.
|
||||
#[test]
|
||||
|
||||
+4
-3
@@ -543,10 +543,11 @@ Owner: open · Status: in progress
|
||||
and add search paths only for a new report that has that output
|
||||
- [ ] Say plainly when what's found is the Claude desktop app rather than the `claude` program
|
||||
(#259, a guess at its cause)
|
||||
- [ ] macOS: `/Applications/Antigravity.app` ended up as the agent's command (#467). Found: the "Where is
|
||||
- [x] macOS: `/Applications/Antigravity.app` ended up as the agent's command (#467). Found: the "Where is
|
||||
the program?" picker (`pick_file`) returns the bundle, and `set_command` saved any path. Refuse a
|
||||
folder or app bundle when it's saved, and don't run one that was saved before — claimed: fabox-os,
|
||||
branch bugfix/467-agent-program-app-bundle
|
||||
folder or app bundle when it's saved (`set_command`), and don't run one that was saved before
|
||||
(`program_of` uses the usual name). Tests: `an_app_bundle_is_refused_as_an_agents_program`, `program_of`
|
||||
cases. — claimed: fabox-os, branch bugfix/467-agent-program-app-bundle
|
||||
- [x] Windows: look in `%LOCALAPPDATA%\agy\bin` too (#62) — claimed: Afloat16, branch bugfix/r2.2-antigravity-search-20261103
|
||||
- [x] Windows: `usual_windows_dirs_come_after_path` counts Antigravity's second folder (#748
|
||||
added it; the test runs only on Windows, which CI doesn't)
|
||||
|
||||
Reference in New Issue
Block a user