diff --git a/Cargo.lock b/Cargo.lock index 13acb75ee146f412f704ed7d2c209ac59928a3cc..294127b598926ddae6924f0af1dd273416cf1e1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13981,6 +13981,7 @@ dependencies = [ "clap", "client", "clock", + "collections", "crash-handler", "crashes", "dap", @@ -14009,6 +14010,7 @@ dependencies = [ "minidumper", "node_runtime", "paths", + "pretty_assertions", "project", "proto", "release_channel", diff --git a/crates/remote_server/Cargo.toml b/crates/remote_server/Cargo.toml index 59b2af0f410c98385cf13a271833980aacd6c8bc..b1f12fb0a8133b95259b38bbec22dbd031937cd7 100644 --- a/crates/remote_server/Cargo.toml +++ b/crates/remote_server/Cargo.toml @@ -77,6 +77,7 @@ assistant_tool.workspace = true assistant_tools.workspace = true client = { workspace = true, features = ["test-support"] } clock = { workspace = true, features = ["test-support"] } +collections.workspace = true dap = { workspace = true, features = ["test-support"] } editor = { workspace = true, features = ["test-support"] } workspace = { workspace = true, features = ["test-support"] } @@ -85,6 +86,7 @@ gpui = { workspace = true, features = ["test-support"] } http_client = { workspace = true, features = ["test-support"] } language = { workspace = true, features = ["test-support"] } node_runtime = { workspace = true, features = ["test-support"] } +pretty_assertions.workspace = true project = { workspace = true, features = ["test-support"] } remote = { workspace = true, features = ["test-support"] } language_model = { workspace = true, features = ["test-support"] } diff --git a/crates/remote_server/src/remote_editing_tests.rs b/crates/remote_server/src/remote_editing_tests.rs index c0ccaf900d18ee176bab7193c2bfb65b8555318d..cb486732c0a0a63e7f6d5d5aed7fe0499ef98b80 100644 --- a/crates/remote_server/src/remote_editing_tests.rs +++ b/crates/remote_server/src/remote_editing_tests.rs @@ -6,6 +6,7 @@ use assistant_tool::{Tool as _, ToolResultContent}; use assistant_tools::{ReadFileTool, ReadFileToolInput}; use client::{Client, UserStore}; use clock::FakeSystemClock; +use collections::{HashMap, HashSet}; use language_model::{LanguageModelRequest, fake_provider::FakeLanguageModel}; use extension::ExtensionHostProxy; @@ -20,6 +21,7 @@ use lsp::{CompletionContext, CompletionResponse, CompletionTriggerKind, Language use node_runtime::NodeRuntime; use project::{ Project, ProjectPath, + agent_server_store::AgentServerCommand, search::{SearchQuery, SearchResult}, }; use remote::RemoteClient; @@ -27,7 +29,6 @@ use serde_json::json; use settings::{Settings, SettingsLocation, SettingsStore, initial_server_settings_content}; use smol::stream::StreamExt; use std::{ - collections::HashSet, path::{Path, PathBuf}, sync::Arc, }; @@ -1770,6 +1771,91 @@ async fn test_remote_agent_fs_tool_calls(cx: &mut TestAppContext, server_cx: &mu does_not_exist_result.output.await.unwrap_err(); } +#[gpui::test] +async fn test_remote_external_agent_server( + cx: &mut TestAppContext, + server_cx: &mut TestAppContext, +) { + let fs = FakeFs::new(server_cx.executor()); + fs.insert_tree(path!("/project"), json!({})).await; + + let (project, _headless_project) = init_test(&fs, cx, server_cx).await; + project + .update(cx, |project, cx| { + project.find_or_create_worktree(path!("/project"), true, cx) + }) + .await + .unwrap(); + let names = project.update(cx, |project, cx| { + project + .agent_server_store() + .read(cx) + .external_agents() + .map(|name| name.to_string()) + .collect::>() + }); + pretty_assertions::assert_eq!(names, ["gemini", "claude"]); + server_cx.update_global::(|settings_store, cx| { + settings_store + .set_raw_server_settings( + Some(json!({ + "agent_servers": { + "foo": { + "command": "foo-cli", + "args": ["--flag"], + "env": { + "VAR": "val" + } + } + } + })), + cx, + ) + .unwrap(); + }); + server_cx.run_until_parked(); + cx.run_until_parked(); + let names = project.update(cx, |project, cx| { + project + .agent_server_store() + .read(cx) + .external_agents() + .map(|name| name.to_string()) + .collect::>() + }); + pretty_assertions::assert_eq!(names, ["gemini", "foo", "claude"]); + let (command, root, login) = project + .update(cx, |project, cx| { + project.agent_server_store().update(cx, |store, cx| { + store + .get_external_agent(&"foo".into()) + .unwrap() + .get_command( + None, + HashMap::from_iter([("OTHER_VAR".into(), "other-val".into())]), + None, + None, + &mut cx.to_async(), + ) + }) + }) + .await + .unwrap(); + assert_eq!( + command, + AgentServerCommand { + path: "ssh".into(), + args: vec!["foo-cli".into(), "--flag".into()], + env: Some(HashMap::from_iter([ + ("VAR".into(), "val".into()), + ("OTHER_VAR".into(), "other-val".into()) + ])) + } + ); + assert_eq!(&PathBuf::from(root), paths::home_dir()); + assert!(login.is_none()); +} + pub async fn init_test( server_fs: &Arc, cx: &mut TestAppContext, diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index abce84daf7cf2b0adc304894476d9c763e5e1a3d..e06e423e92aa3fe093c69d8436545d4a13d17a82 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -487,6 +487,17 @@ impl SettingsStore { Ok(()) } + /// Replaces current settings with the values from the given JSON. + pub fn set_raw_server_settings( + &mut self, + new_settings: Option, + cx: &mut App, + ) -> Result<()> { + self.raw_server_settings = new_settings; + self.recompute_values(None, cx)?; + Ok(()) + } + /// Get the configured settings profile names. pub fn configured_settings_profiles(&self) -> impl Iterator { self.raw_user_settings