diff --git a/build/azure-pipelines/win32/product-build-win32-cli.yml b/build/azure-pipelines/win32/product-build-win32-cli.yml index 78461a959ed..20e49d34866 100644 --- a/build/azure-pipelines/win32/product-build-win32-cli.yml +++ b/build/azure-pipelines/win32/product-build-win32-cli.yml @@ -120,6 +120,9 @@ jobs: SYSTEM_ACCESSTOKEN: $(System.AccessToken) displayName: ✍️ Codesign + - powershell: Remove-Item -Path "$(Build.ArtifactStagingDirectory)/sign/CodeSignSummary*.md" -Force -ErrorAction SilentlyContinue + displayName: Remove CodeSignSummary + - task: ArchiveFiles@2 displayName: Archive signed CLI inputs: diff --git a/build/next/index.ts b/build/next/index.ts index 77388b57a26..565bafc72ec 100644 --- a/build/next/index.ts +++ b/build/next/index.ts @@ -129,6 +129,7 @@ const serverEntryPoints = [ 'vs/workbench/api/node/extensionHostProcess', 'vs/platform/files/node/watcher/watcherMain', 'vs/platform/terminal/node/ptyHostMain', + 'vs/platform/agentHost/node/agentHostMain', ]; // Bootstrap files per target diff --git a/cli/src/commands/agent_host.rs b/cli/src/commands/agent_host.rs index b5330e4df76..955e13f7c68 100644 --- a/cli/src/commands/agent_host.rs +++ b/cli/src/commands/agent_host.rs @@ -21,7 +21,7 @@ use crate::constants::VSCODE_CLI_QUALITY; use crate::download_cache::DownloadCache; use crate::log; use crate::options::Quality; -use crate::tunnels::paths::SERVER_FOLDER_NAME; +use crate::tunnels::paths::{get_server_folder_name, SERVER_FOLDER_NAME}; use crate::tunnels::shutdown_signal::ShutdownRequest; use crate::update_service::{ unzip_downloaded_release, Platform, Release, TargetKind, UpdateService, @@ -74,8 +74,13 @@ pub async fn agent_host(ctx: CommandContext, mut args: AgentHostArgs) -> Result< // Eagerly resolve the latest version so the first connection is fast. // Skip when using a dev override since updates don't apply. if option_env!("VSCODE_CLI_OVERRIDE_SERVER_PATH").is_none() { - if let Err(e) = manager.get_latest_release().await { - warning!(ctx.log, "Error resolving initial server version: {}", e); + match manager.get_latest_release().await { + Ok(release) => { + if let Err(e) = manager.ensure_downloaded(&release).await { + warning!(ctx.log, "Error downloading latest server version: {}", e); + } + } + Err(e) => warning!(ctx.log, "Error resolving initial server version: {}", e), } // Start background update checker @@ -253,9 +258,12 @@ impl AgentHostManager { cmd.stdin(std::process::Stdio::null()); cmd.stderr(std::process::Stdio::piped()); cmd.stdout(std::process::Stdio::piped()); + cmd.arg("--socket-path"); + cmd.arg(get_socket_name()); cmd.arg("--agent-host-path"); cmd.arg(&agent_host_socket); cmd.args([ + "--start-server", "--accept-server-license-terms", "--enable-remote-auto-shutdown", ]); @@ -394,7 +402,8 @@ impl AgentHostManager { // Best case: the latest known release is already downloaded if let Some((_, release)) = &*self.latest_release.lock().await { - if let Some(dir) = self.cache.exists(&release.commit) { + let name = get_server_folder_name(release.quality, &release.commit); + if let Some(dir) = self.cache.exists(&name) { return Ok((release.clone(), dir)); } } @@ -405,15 +414,23 @@ impl AgentHostManager { Quality::try_from(q).map_err(|_| CodeError::UpdatesNotConfigured("unknown quality")) })?; - // Fall back to any cached version (still instant, just not the newest) - for commit in self.cache.get() { - if let Some(dir) = self.cache.exists(&commit) { + // Fall back to any cached version (still instant, just not the newest). + // Cache entries are named "-" via get_server_folder_name. + for entry in self.cache.get() { + if let Some(dir) = self.cache.exists(&entry) { + let (entry_quality, commit) = match entry.split_once('-') { + Some((q, c)) => match Quality::try_from(q.to_lowercase().as_str()) { + Ok(parsed) => (parsed, c.to_string()), + Err(_) => (quality, entry.clone()), + }, + None => (quality, entry.clone()), + }; let release = Release { name: String::new(), commit, platform: self.platform, target: TargetKind::Server, - quality, + quality: entry_quality, }; return Ok((release, dir)); } @@ -428,7 +445,8 @@ impl AgentHostManager { /// Ensures the release is downloaded, returning the server directory. async fn ensure_downloaded(&self, release: &Release) -> Result { - if let Some(dir) = self.cache.exists(&release.commit) { + let cache_name = get_server_folder_name(release.quality, &release.commit); + if let Some(dir) = self.cache.exists(&cache_name) { return Ok(dir); } @@ -436,9 +454,8 @@ impl AgentHostManager { let release = release.clone(); let log = self.log.clone(); let update_service = self.update_service.clone(); - let commit = release.commit.clone(); self.cache - .create(&commit, |target_dir| async move { + .create(&cache_name, |target_dir| async move { let tmpdir = tempfile::tempdir().unwrap(); let response = update_service.get_download_stream(&release).await?; let name = response.url_path_basename().unwrap(); @@ -449,7 +466,8 @@ impl AgentHostManager { response, ) .await?; - unzip_downloaded_release(&archive_path, &target_dir, SilentCopyProgress())?; + let server_dir = target_dir.join(SERVER_FOLDER_NAME); + unzip_downloaded_release(&archive_path, &server_dir, SilentCopyProgress())?; Ok(()) }) .await @@ -504,7 +522,8 @@ impl AgentHostManager { }; // Check if we already have this version - if self.cache.exists(&new_release.commit).is_some() { + let name = get_server_folder_name(new_release.quality, &new_release.commit); + if self.cache.exists(&name).is_some() { continue; } @@ -562,7 +581,10 @@ async fn handle_request( let rw = match get_socket_rw_stream(&socket_path).await { Ok(rw) => rw, Err(e) => { - error!(manager.log, "Error connecting to agent host socket: {:?}", e); + error!( + manager.log, + "Error connecting to agent host socket: {:?}", e + ); return Ok(Response::builder() .status(503) .body(Body::from(format!("Error connecting to agent host: {e:?}"))) diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index 53e4b5f77f2..72752cf9ed1 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -24,6 +24,7 @@ export interface NativeParsedArgs { }; }; 'serve-web'?: INativeCliOptions; + 'agent-host'?: INativeCliOptions; chat?: { _: string[]; 'add-file'?: string[]; diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 7fbfac5b2b0..3b7f625a6ab 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -45,7 +45,7 @@ export type OptionDescriptions = { Subcommand }; -export const NATIVE_CLI_COMMANDS = ['tunnel', 'serve-web'] as const; +export const NATIVE_CLI_COMMANDS = ['tunnel', 'serve-web', 'agent-host'] as const; export const OPTIONS: OptionDescriptions> = { 'chat': { @@ -71,6 +71,15 @@ export const OPTIONS: OptionDescriptions> = { 'telemetry-level': { type: 'string' }, } }, + 'agent-host': { + type: 'subcommand', + description: 'Run a server that hosts agents.', + options: { + 'cli-data-dir': { type: 'string', args: 'dir', description: localize('cliDataDir', "Directory where CLI metadata should be stored.") }, + 'disable-telemetry': { type: 'boolean' }, + 'telemetry-level': { type: 'string' }, + } + }, 'tunnel': { type: 'subcommand', description: 'Make the current machine accessible from vscode.dev or other machines through a secure tunnel.',