File: Program.cs
Web Access
Project: src\src\Aspire.Managed\Aspire.Managed.csproj (aspire-managed)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Aspire.Dashboard;
using Aspire.Managed.NuGet.Commands;
using Aspire.TerminalHost;
using Aspire.Shared;
using System.CommandLine;
 
BundleVersionLease? acquiredBundleLease;
try
{
    acquiredBundleLease = BundleVersionLease.TryAcquireFromEnvironment("aspire-managed", args.FirstOrDefault());
}
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or DirectoryNotFoundException or ArgumentException or NotSupportedException)
{
    Console.Error.WriteLine($"Failed to acquire Aspire bundle lease: {ex.Message}");
    return 1;
}
 
using var bundleLease = acquiredBundleLease;
 
return args switch
{
    ["dashboard", .. var rest] => await RunDashboard(rest).ConfigureAwait(false),
    ["server", .. var rest] => await RunServer(rest).ConfigureAwait(false),
    ["nuget", .. var rest] => await RunNuGet(rest).ConfigureAwait(false),
    ["terminalhost", .. var rest] => await RunTerminalHost(rest).ConfigureAwait(false),
    _ => ShowUsage()
};
 
static async Task<int> RunDashboard(string[] args)
{
    var options = new WebApplicationOptions
    {
        Args = args,
        ContentRootPath = AppContext.BaseDirectory
    };
 
    var app = new DashboardWebApplication(options: options);
 
    // Tear the dashboard down if the launching CLI dies so a hard-killed `aspire dashboard run` (or the
    // profiling collector, which also launches aspire-managed dashboard) cannot leave an orphaned
    // dashboard process behind. No-op when ASPIRE_CLI_PID is not set — either the dashboard is launched
    // directly (so the embedded/in-process dashboard is unaffected), or on Windows where the CLI relies
    // on the kernel kill-on-close job instead (see LayoutProcessRunner).
    using var shutdownCts = new CancellationTokenSource();
    var parentWatchdog = ParentProcessWatchdog.Start(shutdownCts);
    try
    {
        return await app.RunAsync(shutdownCts.Token).ConfigureAwait(false);
    }
    finally
    {
        if (parentWatchdog is not null)
        {
            await parentWatchdog.DisposeAsync().ConfigureAwait(false);
        }
    }
}
 
static async Task<int> RunServer(string[] args)
{
    await Aspire.Hosting.RemoteHost.RemoteHostServer.RunAsync(args).ConfigureAwait(false);
    return 0;
}
 
static async Task<int> RunNuGet(string[] args)
{
    // Tear this helper down if the launching CLI dies so a hung/slow NuGet operation cannot linger as an
    // orphaned aspire-managed process. No-op when ASPIRE_CLI_PID is not set — either invoked directly, or
    // on Windows where the CLI relies on the kernel kill-on-close job instead (see LayoutProcessRunner).
    using var operationCts = new CancellationTokenSource();
    var parentWatchdog = ParentProcessWatchdog.Start(operationCts);
    try
    {
        var rootCommand = new RootCommand("Aspire NuGet Helper - Package operations for Aspire CLI bundle");
        rootCommand.Subcommands.Add(SearchCommand.Create());
        rootCommand.Subcommands.Add(RestoreCommand.Create());
        rootCommand.Subcommands.Add(LayoutCommand.Create());
        rootCommand.Subcommands.Add(ManifestCommand.Create());
        return await rootCommand.Parse(args).InvokeAsync(cancellationToken: operationCts.Token).ConfigureAwait(false);
    }
    finally
    {
        if (parentWatchdog is not null)
        {
            await parentWatchdog.DisposeAsync().ConfigureAwait(false);
        }
    }
}
 
static async Task<int> RunTerminalHost(string[] args)
{
    return await TerminalHostProcessRunner.RunAsync(args).ConfigureAwait(false);
}
 
static int ShowUsage()
{
    Console.Error.WriteLine($"Usage: {AppDomain.CurrentDomain.FriendlyName} <dashboard|server|nuget|terminalhost> [args...]");
    return 1;
}